reading processed data by CarDEC
adata=ad$read_h5ad("../CarDEC Results/adata_CarDEC.h5ad")
cell.meta.data=py_to_r(adata$obs)
cell.meta.data$dataset_batch=plyr::mapvalues(cell.meta.data$batch_label,names(maprules),maprules)
gene_ann0=py_to_r(adata$var)
mtx=t(py_to_r(adata$layers['denoised counts']))
colnames(mtx)=cell.meta.data$cellname
rownames(mtx)=rownames(gene_ann0)
mtx_sizefactor=1e4/colSums(mtx)
obj1=CreateSeuratObject(mtx,meta.data = cell.meta.data)
obj1=NormalizeData(obj1,verbose = F)
Idents(obj1)="BatchID" #obj1 means denoised count by CarDEC
avg_exp=Seurat:::FastExpMean(obj1@assays$RNA@counts,display_progress = F)#log1p(
gene_ann_all=data.frame(gene_short_name = make.unique(rownames(gene_ann0)),
VarianceType=gene_ann0$`Variance Type`,
avg_exp=avg_exp,
row.names = make.unique(rownames(gene_ann0)))
#assign pseudotime for obj0
cds=readRDS("./cds_cardec.rds") #csd_cardec is the monocle's output from CarDEC
xtmp=colData(cds)[,c("Pseudotime")]%>%as.data.frame()
obj0$Pseudotime=colData(cds)$Pseudotime
obj1$Pseudotime=colData(cds)$Pseudotime
obj0=NormalizeData(obj0,verbose = F)
obj0=FindVariableFeatures(obj0,verbose = F)
xx=obj0@assays$RNA@meta.features[,c("vst.mean"),drop=F]
xx$gene=rownames(xx)
We obtained TFs related to monocytes from Figure 4 in Gene expression profiling reveals the defining features of the classical, intermediate, and nonclassical human monocyte subsets
tf_df=openxlsx::read.xlsx("./TF_blood.xlsx")%>%filter(Gene%in%rownames(obj0))%>%
left_join(gene_ann_all,by=c("Gene"="gene_short_name"))%>%
left_join(xx,by=c("Gene"="gene"))%>%
as.data.frame()
Column `Gene`/`gene_short_name` joining character vector and factor, coercing into character vector
rownames(tf_df)=tf_df$Gene
tf_df
num_cells_expressed=Matrix::rowSums(obj0@assays$RNA@counts[tf_df$Gene,]!=0)
tf_df$num_cells_expressed=num_cells_expressed[rownames(tf_df)]
Heatmap-no smoothed
Note: genes with color blue means LVGs and genes with red means HVGs. We firstly filtered out genes with average expression lower than 0.1 and sorted the genes decreasingly by average expression grouped by in each gene class (I set 3 clusters for gene, but didn’t show here) and highlighted the top 15 genes.
pseudotime from cardec’s denoised gene expression
obj1=NormalizeData(obj1,verbose = F)
obj1=FindVariableFeatures(obj1,verbose = F)
hvf.info=obj1@assays$RNA@meta.features
gene22=hvf.info[order(hvf.info$vst.variance.standardized, decreasing = TRUE), ,drop = FALSE]
num_cells_expressed=Matrix::rowSums(obj1@assays$RNA@counts[tf_df$Gene,]!=0)
obj1=ScaleData(obj1,features = tf_df$Gene,verbose = F)
#scaled
#m = t(scale(m1,center = T))
m=t(FetchData(obj1,vars = tf_df$Gene,slot = "scale.data"))
m[is.nan(m)] = 0
m[m >= 3] = 3
m[m <= -3] = -3
heatmap_matrix <- m
row_dist <- as.dist((1 - cor(t(heatmap_matrix)))/2)
res2 <- list(ph=pheatmap::pheatmap(heatmap_matrix, useRaster = T, cluster_cols = FALSE,
cluster_rows = T, show_rownames = F, show_colnames = F,
clustering_distance_rows = row_dist, clustering_method = "ward.D2",
cutree_rows = 3, silent = TRUE, filename = NA),m=heatmap_matrix)
z=res2[[2]][res2[[1]]$tree_row$order,]
Cluster_labels=cutree(res2[[1]]$tree_row,3)[res2[[1]]$tree_row$order]
row_anno=data.frame(genename=rownames(z),
Cluster=Cluster_labels,
Class=tf_df[rownames(z),"Gene"],
VarianceType=tf_df[rownames(z),"VarianceType"],
GeneType=tf_df[rownames(z),"GeneType"],
#avg_exp=tf_df[rownames(z),"avg_exp"],
vst.mean=gene22[rownames(z),"vst.mean"],
vst.variance.standard=gene22[rownames(z),"vst.variance.standardized"],
num_cells_expressed=num_cells_expressed[rownames(z)],
row.names = rownames(z),stringsAsFactors = F)
row_anno$GeneClass=plyr::mapvalues(row_anno$Cluster,from=sort(unique(row_anno$Cluster)),
to=paste0("Module",as.numeric(factor(sort(unique(Cluster_labels))))))
#order_id0=order(row_anno$Cluster,row_anno$GeneType,row_anno$VarianceType)
#z=z[order_id0,]
#row_anno=row_anno[order_id0,]
#GeneClass.color=gg_color_hue(length(unique(Cluster_labels)))
GeneClass.color=RColorBrewer::brewer.pal(3,"Dark2")
names(GeneClass.color)=paste0("Module",as.numeric(factor(sort(unique(Cluster_labels)))))
# gene Module color
row_anno$color=plyr::mapvalues(row_anno$GeneClass,from=names(GeneClass.color),
to=GeneClass.color)
row_anno$HVG_color=plyr::mapvalues(as.character(row_anno$VarianceType),from=c("HVG","LVG"),
to=c("#E41A1C","#377EB8"))
rownames(row_anno)=row_anno$genename
pseudotime_col_fun =circlize::colorRamp2(seq(0, 1,length=150), colorRampPalette(monocle:::blue2green2red(150))(150))
# columan annotation
#col_anno=data.frame(pseudotime=seq(0,1,length=ncol(z)),
# row.names = as.character(1:ncol(z)))
col_anno=data.frame(BatchID=plyr::mapvalues(obj1$BatchID,c("MH001","RP002","RP009"),c("T1","T2","T3")),
Pseudotime=obj1$Pseudotime,
row.names = colnames(obj1))
col_id=order(col_anno$Pseudotime)
z=z[,col_id]
col_anno=col_anno[col_id,]
#
set.seed(10)
row_anno.tmp=row_anno%>%
filter(vst.mean>0.1)%>%
group_by(VarianceType)%>%
arrange(desc(vst.mean),.by_group = TRUE)%>%
mutate(n_order=1:n())%>%as.data.frame()
#id.select=order(row_anno$qval,decreasing = F)[1:30]
id.select=which(row_anno$genename%in%row_anno.tmp$genename[row_anno.tmp$n_order%in%c(1:20)])
row_anno.new=row_anno[id.select,]
col_fun = circlize::colorRamp2(seq(-3, 3,length=200), colorRampPalette(c("blue", "white", "red"))(200))
#pseudotime_col_fun =circlize::colorRamp2(seq(0, 1,length=150), colorRampPalette(c("darkblue","black","#FFFF00"))(150))
pseudotime_col_fun =circlize::colorRamp2(seq(0, 1,length=150), colorRampPalette(monocle:::blue2green2red(150))(150))
top_anno=HeatmapAnnotation( Pseudotime = anno_simple(col_anno$Pseudotime, col = pseudotime_col_fun),
BatchID=col_anno$BatchID,
col=list(BatchID=c("T1"="#66C2A5","T2"="#FC8D62","T3"="#8DA0CB")),
show_legend = c(T,T),
annotation_name_side = "right")
left_anno=rowAnnotation(#GeneClass=row_anno$GeneClass,
#GeneType=row_anno$GeneType,
VarianceType=row_anno$VarianceType,
#Nonzero=anno_barplot(log10(row_anno$num_cells_expressed),
# gp = gpar(fill ="#00C4FF",col=NA),
# bar_width = 1,
# height = unit(2.5, "cm")),
annotation_name_rot=90,
annotation_name_side = "top",
#col=list(GeneClass=celltype_color[names(celltype_color)%in%unique(tmp.anno$celltype)]),
col=list(GeneClass=GeneClass.color,
VarianceType=c("HVG"="#E41A1C","LVG"="#377EB8")),
show_annotation_name=c(F,T),
show_legend=c(T,T))
p1=ComplexHeatmap::Heatmap(z, name = "scaled.expression",
cluster_rows = F,
cluster_columns = F,
col=col_fun,
column_labels = rep("",length=ncol(z)),
row_labels = rep("",length=nrow(z)),
#row_labels = gene.use.df$gene,
row_names_side = "right",
#the following fontsize is nonusefull for this situattion
row_names_gp = gpar(fontsize=5),
column_names_gp = gpar(fontsize = 8),
#column_gap = unit(0.5, "mm"),
#column_split = tmp.anno$res,
#column_title = "%s",
#column_title_rot=90,
#row_gap = unit(0.5, "mm"),
#row_split = row_anno$GeneClass,
#row_title = "%s",
row_title_gp = gpar(fontsize = 12),
row_title_rot = 90,
top_annotation = top_anno,
left_annotation= left_anno,
heatmap_legend_param=list(legend_direction="horizontal",legend_width = unit(2.5, "cm")),
right_annotation = rowAnnotation(foo=anno_mark(at=id.select,
side="right",
extend=unit(0.2,"cm"),
labels_gp = gpar(fontsize=8,col=row_anno$HVG_color[id.select]),
labels=row_anno.new$genename),
annotation_name_side="top"))
#draw(p1,padding=unit(c(2,2,2,2),"mm"))
lgd_pse=Legend(title = "Pseudotime", col = pseudotime_col_fun, at = c(0,0.5,1),
labels = c("low","med","high"),legend_height = unit(2.5, "cm"),border = NA,title_position = "topcenter",direction="horizontal")
draw(p1,annotation_legend_list = list(lgd_pse),merge_legends=T,heatmap_legend_side="bottom")

pseudotime from raw gene expression
cds=readRDS("./cds_raw.rds")
print(sum(colnames(obj0)==colnames(cds)))
[1] 10878
#10878
obj0$Pseudotime_raw=colData(cds)$Pseudotime
obj0=NormalizeData(obj0,verbose = F)
obj0=FindVariableFeatures(obj0,verbose = F)
hvf.info=obj0@assays$RNA@meta.features
gene22=hvf.info[order(hvf.info$vst.variance.standardized, decreasing = TRUE), ,drop = FALSE]
num_cells_expressed=Matrix::rowSums(obj0@assays$RNA@counts[tf_df$Gene,]!=0)
obj0=ScaleData(obj0,features = tf_df$Gene,verbose = F)
#scaled
#m = t(scale(m1,center = T))
m=t(FetchData(obj0,vars = tf_df$Gene,slot = "scale.data"))
m[is.nan(m)] = 0
m[m >= 3] = 3
m[m <= -3] = -3
heatmap_matrix <- m
row_dist <- as.dist((1 - cor(t(heatmap_matrix)))/2)
res2 <- list(ph=pheatmap::pheatmap(heatmap_matrix, useRaster = T, cluster_cols = FALSE,
cluster_rows = T, show_rownames = F, show_colnames = F,
clustering_distance_rows = row_dist, clustering_method = "ward.D2",
cutree_rows = 3, silent = TRUE, filename = NA),m=heatmap_matrix)
z=res2[[2]][res2[[1]]$tree_row$order,]
Cluster_labels=cutree(res2[[1]]$tree_row,3)[res2[[1]]$tree_row$order]
row_anno=data.frame(genename=rownames(z),
Cluster=Cluster_labels,
Class=tf_df[rownames(z),"Gene"],
VarianceType=tf_df[rownames(z),"VarianceType"],
GeneType=tf_df[rownames(z),"GeneType"],
vst.mean=gene22[rownames(z),"vst.mean"],
vst.variance.standard=gene22[rownames(z),"vst.variance.standardized"],
num_cells_expressed=num_cells_expressed[rownames(z)],
row.names = rownames(z),stringsAsFactors = F)
row_anno$GeneClass=plyr::mapvalues(row_anno$Cluster,from=sort(unique(row_anno$Cluster)),
to=paste0("Module",as.numeric(factor(sort(unique(Cluster_labels))))))
#order_id0=order(row_anno$Cluster,row_anno$GeneType,row_anno$VarianceType)
#z=z[order_id0,]
#row_anno=row_anno[order_id0,]
#GeneClass.color=gg_color_hue(length(unique(Cluster_labels)))
GeneClass.color=RColorBrewer::brewer.pal(3,"Dark2")
names(GeneClass.color)=paste0("Module",as.numeric(factor(sort(unique(Cluster_labels)))))
# gene Module color
row_anno$color=plyr::mapvalues(row_anno$GeneClass,from=names(GeneClass.color),
to=GeneClass.color)
row_anno$HVG_color=plyr::mapvalues(as.character(row_anno$VarianceType),from=c("HVG","LVG"),
to=c("#E41A1C","#377EB8"))
rownames(row_anno)=row_anno$genename
pseudotime_col_fun =circlize::colorRamp2(seq(0, 1,length=150), colorRampPalette(monocle:::blue2green2red(150))(150))
# columan annotation
#col_anno=data.frame(pseudotime=seq(0,1,length=ncol(z)),
# row.names = as.character(1:ncol(z)))
col_anno=data.frame(BatchID=plyr::mapvalues(obj0$BatchID,c("MH001","RP002","RP009"),c("T1","T2","T3")),
Pseudotime=obj0$Pseudotime_raw,
#Pseudotime=obj0$Pseudotime,
row.names = colnames(obj0))
col_id=order(col_anno$Pseudotime)
z=z[,col_id]
col_anno=col_anno[col_id,]
#
set.seed(10)
row_anno.tmp=row_anno%>%
filter(vst.mean>0.1)%>%
group_by(VarianceType)%>%
arrange(desc(vst.mean),.by_group = TRUE)%>%
mutate(n_order=1:n())%>%as.data.frame()
#id.select=order(row_anno$qval,decreasing = F)[1:30]
id.select=which(row_anno$genename%in%row_anno.tmp$genename[row_anno.tmp$n_order%in%c(1:20)])
row_anno.new=row_anno[id.select,]
col_fun = circlize::colorRamp2(seq(-3, 3,length=200), colorRampPalette(c("blue", "white", "red"))(200))
#pseudotime_col_fun =circlize::colorRamp2(seq(0, 1,length=150), colorRampPalette(c("darkblue","black","#FFFF00"))(150))
pseudotime_col_fun =circlize::colorRamp2(seq(0, 1,length=150), colorRampPalette(monocle:::blue2green2red(150))(150))
top_anno=HeatmapAnnotation( Pseudotime = anno_simple(col_anno$Pseudotime, col = pseudotime_col_fun),
BatchID=col_anno$BatchID,
col=list(BatchID=c("T1"="#66C2A5","T2"="#FC8D62","T3"="#8DA0CB")),
show_legend = c(T,T),
annotation_name_side = "right")
left_anno=rowAnnotation(#GeneClass=row_anno$GeneClass,
#GeneType=row_anno$GeneType,
VarianceType=row_anno$VarianceType,
#Nonzero=anno_barplot(log10(row_anno$num_cells_expressed),
# gp = gpar(fill ="#00C4FF",col=NA),
# bar_width = 1,
# height = unit(2.5, "cm")),
annotation_name_rot=90,
annotation_name_side = "top",
#col=list(GeneClass=celltype_color[names(celltype_color)%in%unique(tmp.anno$celltype)]),
col=list(GeneClass=GeneClass.color,
VarianceType=c("HVG"="#E41A1C","LVG"="#377EB8")),
show_annotation_name=c(F,T),
show_legend=c(T,T))
p2=ComplexHeatmap::Heatmap(z, name = "scaled.expression",
cluster_rows = F,
cluster_columns = F,
col=col_fun,
column_labels = rep("",length=ncol(z)),
row_labels = rep("",length=nrow(z)),
#row_labels = gene.use.df$gene,
row_names_side = "right",
#the following fontsize is nonusefull for this situattion
row_names_gp = gpar(fontsize=5),
column_names_gp = gpar(fontsize = 8),
#column_gap = unit(0.5, "mm"),
#column_split = tmp.anno$res,
#column_title = "%s",
#column_title_rot=90,
#row_split =gene.use.df$cluster,
row_gap = unit(0.5, "mm"),
#row_split = row_anno$GeneClass,
#row_title = "%s",
row_title_gp = gpar(fontsize = 12),
row_title_rot = 90,
top_annotation = top_anno,
left_annotation= left_anno,
heatmap_legend_param=list(legend_direction="horizontal",legend_width = unit(2.5, "cm")),
right_annotation = rowAnnotation(foo=anno_mark(at=id.select,
side="right",
extend=unit(0.2,"cm"),
labels_gp = gpar(fontsize=8,col=row_anno$HVG_color[id.select]),
labels=row_anno.new$genename),
annotation_name_side="top"))
#draw(p1,padding=unit(c(2,2,2,2),"mm"))
lgd_pse=Legend(title = "Pseudotime", col = pseudotime_col_fun, at = c(0,0.5,1),
labels = c("low","med","high"),legend_height = unit(2.5, "cm"),border = NA,title_position = "topcenter",direction="horizontal")
draw(p2,annotation_legend_list = list(lgd_pse),merge_legends=T,heatmap_legend_side="bottom")

reading results from other methods
ad=import("anndata",convert = FALSE)
adata=ad$read_h5ad("../../dca_test.h5ad")
obj_raw=Convert_to_seurat3(adata)
obj_raw=NormalizeData(obj_raw,verbose = F)
obj_raw=FindVariableFeatures(obj_raw,verbose = F)
xx_vst.mean=obj_raw@assays$RNA@meta.features[,c("vst.mean"),drop=F]
xx_vst.mean$gene=rownames(xx_vst.mean)
raw.data=obj_raw@assays$RNA@counts
maprules=c("2017_0801"="T1","2017_1017"="T2","2017_1120"="T3")
maprules
2017_0801 2017_1017 2017_1120
"T1" "T2" "T3"
Methods_color=c("#E41A1C","#377EB8","#4DAF4A","#984EA3","#FF7F00","#00cc99")
names(Methods_color)=c("scVI","CarDEC","DCA","MNN","Raw","Scanorama")
op=par(mar=c(5,4,6,4))
image(1:length(Methods_color),1, as.matrix(1:length(Methods_color)),col=Methods_color,xlab = "", ylab = "")
axis(3,at=seq(1:length(Methods_color)),labels=Methods_color,las=2,lwd=0)
par(op)

adata=ad$read_h5ad("../CarDEC Results/adata_CarDEC.h5ad")
cell.meta.data=py_to_r(adata$obs)
cell.meta.data$dataset_batch=plyr::mapvalues(cell.meta.data$batch_label,names(maprules),maprules)
gene_ann_cardec=py_to_r(adata$var)
mtx=t(py_to_r(adata$layers['denoised counts']))
colnames(mtx)=cell.meta.data$cellname
rownames(mtx)=rownames(gene_ann_cardec)
mtx_sizefactor=1e4/colSums(mtx)
obj_cardec=CreateSeuratObject(mtx,meta.data = cell.meta.data)
Invalid name supplied, making object name syntactically valid. New object name is batch_labelcellnamedataset_batchdataset_labelstatus_labeln_genesn_countspercent_mitoBatchIDsize.factorsbatch; see ?make.names for more details on syntax validity
obj_cardec=NormalizeData(obj_cardec,verbose = F)
Idents(obj_cardec)="BatchID" #obj1 means denoised count by CarDEC
suppressPackageStartupMessages(library(monocle3))
cds=readRDS("./cds_cardec.rds")
obj_cardec$Pseudotime=colData(cds)$Pseudotime
#/max(colData(cds)$Pseudotime,na.rm=T)
obj_raw$Pseudotime=colData(cds)$Pseudotime
#/max(colData(cds)$Pseudotime,na.rm=T)
rm(cds)
gc()
used (Mb) gc trigger (Mb) max used (Mb)
Ncells 8292993 442.9 14413331 769.8 14413331 769.8
Vcells 994980592 7591.1 1631060878 12444.1 1510425451 11523.7
cds=readRDS("./cds_raw.rds")
obj_raw$Pseudotime_raw=colData(cds)$Pseudotime
#/max(colData(cds)$Pseudotime,na.rm=T)
DCA+combat
adata=ad$read_h5ad("../final_processed_results/dca Results New/adata_all.h5ad")
cell.meta.data=py_to_r(adata$obs)
cell.meta.data$dataset_batch=plyr::mapvalues(cell.meta.data$batch_label,names(maprules),maprules)
gene_ann0=py_to_r(adata$var)
gene_ann=data.frame(gene_short_name = make.unique(rownames(gene_ann0)),
row.names = make.unique(rownames(gene_ann0)))
mtx=t(py_to_r(adata$X))
colnames(mtx)=cell.meta.data$cellname
rownames(mtx)=rownames(gene_ann)
mtx_sizefactor=1e4/colSums(mtx)
obj_dca=CreateSeuratObject(mtx,meta.data=cell.meta.data)
#obj_dca=NormalizeData(object = obj_dca,verbose = F)
cds=readRDS("./cds_dca.rds")
obj_dca$Pseudotime=colData(cds)$Pseudotime
#/max(colData(cds)$Pseudotime,na.rm=T)
scVI
adata=ad$read_h5ad("../final_processed_results/scVI Results New/monocytes_ALL/adata_all.h5ad")
cell.meta.data=py_to_r(adata$obs)
cell.meta.data$dataset_batch=plyr::mapvalues(cell.meta.data$batch_label,names(maprules),maprules)
gene_ann0=py_to_r(adata$var)
gene_ann=data.frame(gene_short_name = make.unique(rownames(gene_ann0)),
row.names = make.unique(rownames(gene_ann0)))
mtx=t(py_to_r(adata$X))
colnames(mtx)=cell.meta.data$cellname
rownames(mtx)=rownames(gene_ann)
mtx_sizefactor=1e4/colSums(mtx)
obj_scvi=CreateSeuratObject(mtx,meta.data=cell.meta.data)
Invalid name supplied, making object name syntactically valid. New object name is batch_labelcellnamedataset_batchdataset_labelstatus_labeln_genesn_countspercent_mitoBatchIDBatchID_encodeX_scvi_batchX_scvi_labelsX_scvi_local_l_meanX_scvi_local_l_varlouvain_denoisedlouvain_latent; see ?make.names for more details on syntax validity
#obj_scvi=NormalizeData(object = obj_scvi,verbose = F)
cds=readRDS("./cds_scvi.rds")
obj_scvi$Pseudotime=colData(cds)$Pseudotime
#/max(colData(cds)$Pseudotime,na.rm = T)
MNN
output=readRDS("../final_processed_results/MNN_corrected_all.rds")
mtx=output@assays$data$corrected
cell.meta.data=colData(output)
cell.meta.data$dataset_batch=plyr::mapvalues(cell.meta.data$batch_label,names(maprules),maprules)
gene_ann=data.frame(gene_short_name = make.unique(rownames(mtx)),
row.names = make.unique(rownames(mtx)))
obj_mnn=CreateSeuratObject(mtx,meta.data=as.data.frame(cell.meta.data))
cds=readRDS("./cds_mnn.rds")
obj_mnn$Pseudotime=colData(cds)$Pseudotime
#/max(colData(cds)$Pseudotime,na.rm=T)
scanorama
#add when revised in Genome Research
adata=ad$read_h5ad("../final_processed_results/scanorama Results/adata_ALL.h5ad")#
cell.meta.data=py_to_r(adata$obs)
cell.meta.data$dataset_batch=plyr::mapvalues(cell.meta.data$batch_label,names(maprules),maprules)
gene_ann0=py_to_r(adata$raw$var)
gene_ann=data.frame(gene_short_name = make.unique(rownames(gene_ann0)),
row.names = make.unique(rownames(gene_ann0)))
mtx=t(py_to_r(adata$raw$X$tocsc()))#
colnames(mtx)=cell.meta.data$cellname
rownames(mtx)=rownames(gene_ann)
obj_scanorama=CreateSeuratObject(mtx,meta.data=as.data.frame(cell.meta.data))
#obj_scanorama=NormalizeData(obj_scanorama,verbose = F)
cds=readRDS("./cds_scanorama.rds")
obj_scanorama$Pseudotime=colData(cds)$Pseudotime
#/max(colData(cds)$Pseudotime,na.rm=T)
avg_exp=log1p(Seurat:::FastExpMean(obj_cardec@assays$RNA@counts,display_progress = F))
gene_ann_all=data.frame(gene_short_name = make.unique(rownames(obj_cardec)),
VarianceType=gene_ann_cardec$`Variance Type`,
avg_exp=avg_exp,
row.names = make.unique(rownames(obj_cardec)))
tf_df=openxlsx::read.xlsx("./TF_blood.xlsx")%>%filter(Gene%in%rownames(obj_raw))%>%
left_join(gene_ann_all,by=c("Gene"="gene_short_name"))%>%
left_join(xx_vst.mean,by=c("Gene"="gene"))%>%
as.data.frame()
Column `Gene`/`gene_short_name` joining character vector and factor, coercing into character vector
rownames(tf_df)=tf_df$Gene
DT::datatable(tf_df)
After filtering out gene expression <0.05, we have
tf_df=subset(tf_df,tf_df$avg_exp>=0.05 )
DT::datatable(tf_df)
#write.table(tf_df[,c(1,2,3)],file = "tmp.csv",sep=",",col.names = T,row.names = F)
get_raw_denoised_df_new=function(obj,gene){
#df0=data.frame(cbind(pseudotime=obj0$Pseudotime,FetchData(obj0,vars=gene)))
df0=FetchData(object = obj,vars=gene)
df0$pseudotime=obj$Pseudotime
df0$Pseudotime_01=obj$Pseudotime/max(obj$Pseudotime,na.rm = T)# for plot
#colnames(df0)[2:ncol(df0)]=gsub(pattern = "-",replacement = "",x =gene )
df0$BatchID=plyr::mapvalues(obj$batch_label,names(maprules),maprules)
df0=df0[is.finite(df0$pseudotime),]
df0=df0[order(df0$pseudotime,decreasing = F),,drop=F]
df0$x=df0$pseudotime/max(df0$pseudotime)
return(df0)
}
suppressPackageStartupMessages(library(mgcv))
get_pval_pair_new=function(df00,gene,return_pval_table=T){
df00_tmp=df00
id_gene=which(colnames(df00_tmp)==gene)
gene_new=gsub("-|\\.","",gene)
colnames(df00_tmp)[id_gene]=gene_new
formula1=as.formula(paste0(gene_new,"~","s(pseudotime,bs=\"cs\")"))
formula2=as.formula(paste0(gene_new,"~BatchID+","s(pseudotime,bs=\"cs\")"))
mod1 <- gam(formula1, data = df00_tmp, select = F)
mod2 <- gam(formula2, data = df00_tmp, select = F)
m1=anova(mod1, mod2, test = "Chisq")
m2=anova(mod1, mod2, test = "F")
unique_batch=sort(as.character(unique(df00_tmp$BatchID)),decreasing = T)
res_pairwise=lapply(unique_batch,function(x){
df0tmp=df00_tmp[df00_tmp$BatchID!=x,,drop=F]
mod1 <- gam(formula1, data = df0tmp, select = F)
mod2 <- gam(formula2, data = df0tmp, select = F)
m1=anova(mod1, mod2, test = "Chisq")
m2=anova(mod1, mod2, test = "F")
return(list(mod1=mod1,mod2=mod2,m1=m1,m2=m2))
})
names(res_pairwise)=unique_batch
if(return_pval_table){
col_names=c("gene",paste0(rep(c("T1 v.s. T2", "T1 v.s. T3","T2 v.s. T3"),times=2),
rep(c(" (Chisq)"," (F)"),each=3)),
"Overall (Chisq)", "Overall (F)"
)
res0=data.frame(matrix(NA,ncol=length(col_names),nrow=1))
colnames(res0)=col_names
res0[1,1]=gene
res0[1,2]=res_pairwise$T3$m1$`Pr(>Chi)`[2]
res0[1,3]=res_pairwise$T2$m1$`Pr(>Chi)`[2]
res0[1,4]=res_pairwise$T1$m1$`Pr(>Chi)`[2]
res0[1,5]=res_pairwise$T3$m2$`Pr(>F)`[2]
res0[1,6]=res_pairwise$T2$m2$`Pr(>F)`[2]
res0[1,7]=res_pairwise$T1$m2$`Pr(>F)`[2]
res0[1,8]=m1$`Pr(>Chi)`[2]
res0[1,9]=m2$`Pr(>F)`[2]
return(res0)
}else{
return(list(single=list(mod1=mod1,mod2=mod2,m1=m1,m2=m2),
pariwise=res_pairwise))
}
}
res_pval_list=readRDS("./res_pval_new_revised.rds")
Final Plots
In this Section, I reported the respective pseudotime identified by compared methods, including MNN, scVI, DCA and scanorama
Feature plots
old=theme_set(theme_bw()+theme(strip.background = element_rect(fill="white"),
panel.background = element_blank(),
legend.background = element_blank(),
panel.grid =element_blank()))
df0_tmp0=subset(df_plot, variable=="Overall (Chisq)" &!Group %in%"Raw")
ggtitle0=c("CarDEC"="obj_cardec","DCA"="obj_dca","MNN"="obj_mnn","Raw"="obj_raw","scVI"="obj_scvi","Scanorama"="obj_scanorama")
get_plot1=function(df00,gene="S100A8",title0="CarDEC"){
p_val_label=df0_tmp0$pval[df0_tmp0$gene==gene&df0_tmp0$Group==title0]
p=ggplot(data =df00,aes_string(x="pseudotime",y=gene))+
geom_point(aes(color=BatchID),size=0.01)+
guides(color=guide_legend(override.aes = list(size=5)))+
geom_smooth(aes(color=BatchID),method="gam",formula = y ~ s(x, bs="cs"))+
geom_smooth(color="black",method="gam",formula = y ~ s(x, bs="cs"),size=0.5)+
ylab(paste0(gene," (",as.character(tf_df$VarianceType[tf_df$Gene==gene]),")"))+
ggtitle(title0,subtitle = paste0("p-value=",ifelse(length(p_val_label)!=0,scales::scientific(p_val_label),"NA")))+
xlab("Pseudotime")+theme(legend.position = "right",
plot.title = element_text(size=18,face="bold",hjust=0.5),
legend.text = element_text(size=15,face="bold"),
axis.title.y = element_text(color=ifelse(as.character(tf_df$VarianceType[tf_df$Gene==gene])=="HVG","red","blue")),
legend.title = element_blank())+scale_color_brewer(palette = "Set2")
return(p)
}
plist_0=lapply(sort(tf_df$Gene),function(gene0){
p1=get_plot1(df0_list$obj_cardec,gene = gene0,title0 = "CarDEC")+theme(legend.position = "none")
p2=get_plot1(df0_list$obj_dca,gene = gene0,title0 = "DCA")+theme(legend.position = "none")
p3=get_plot1(df0_list$obj_mnn,gene = gene0,title0 = "MNN")
p4=get_plot1(df0_list$obj_scvi,gene = gene0,title0 = "scVI")+theme(legend.position = "none")
p5=get_plot1(df0_list$obj_scanorama,gene = gene0,title0 = "Scanorama")+theme(legend.position = "none")
return(list(p1,p5,p2,p4,p3))
})
plist_1=lapply(1:length(plist_0),function(x) egg::ggarrange(plots = plist_0[[x]],nrow = 1,draw = F))
id0=c(23,22,25,19)
plot_grid(plist_1[[23]],plist_1[[22]],plist_1[[25]],plist_1[[19]],align = "v",ncol = 1)

plist_00=lapply(sort(tf_df$Gene)[c(23,22,25,19)],function(gene0){
p1=get_plot1(df0_list$obj_cardec,gene = gene0,title0 = "CarDEC")+theme(legend.position = "none")+
theme(plot.title = element_blank(),plot.margin = unit(c(0.2,0,0.5,0.2),"cm"))
p2=get_plot1(df0_list$obj_dca,gene = gene0,title0 = "DCA")+theme(legend.position = "none")+theme(plot.title = element_blank())
p3=get_plot1(df0_list$obj_mnn,gene = gene0,title0 = "MNN")+theme(plot.title = element_blank())
p4=get_plot1(df0_list$obj_scvi,gene = gene0,title0 = "scVI")+theme(legend.position = "none")+theme(plot.title = element_blank())
p5=get_plot1(df0_list$obj_scanorama,gene = gene0,title0 = "Scanorama")+theme(legend.position = "none")+theme(plot.title = element_blank())
return(list(p1,p5,p2,p4,p3))
})
pp0=egg::ggarrange(plots = c(plist_00[[1]],plist_00[[2]],plist_00[[3]],plist_00[[4]]),ncol = 5,draw = F)
Heatmap plots
suppressPackageStartupMessages(library(cowplot))
p.denoised=ggdraw()+draw_image(magick::image_read_pdf("denoised_heatmap_nosmooth.pdf"),scale=1)
p.raw=ggdraw()+draw_image(magick::image_read_pdf("./raw_pseudotime_heatmap_nosmooth.pdf"),scale=1)
p_heatmap=ggdraw()+draw_plot(p.denoised,x=0,y=0,width=0.5,height=0.98)+
draw_plot(p.raw,x=0.5,y=0,width=0.5,height=0.98)+
draw_label("Denoised gene expression (pseudotime by CarDEC)", x=0.25,y=1,hjust=0.5,vjust = 1,size=20)+
draw_label("Raw gene expression (pseudotime by Raw)", x=0.75,y=1,hjust=0.5,vjust = 1,size=20)
p_heatmap

boxplot of pval
df0_tmp=subset(df_plot,genetype=="HVG" & variable=="Overall (Chisq)" &!Group %in%"Raw")
df0_tmp$Group=factor(df0_tmp$Group,levels = c("CarDEC","Scanorama","DCA","scVI","MNN"))
#df0_tmp%>%group_by(Group)%>%summarise(n=n())
p1=ggplot(df0_tmp,
aes(x=Group,y=pval_log10,fill=Group))+
#geom_violin(aes(x=variable,y=pval),scale = "width",adjust=1)+
geom_boxplot(width=0.5,color="blue",outlier.color = NA,size=0.2,
position = position_dodge(0.6))+
#ggrepel::geom_label_repel(data=df_plot_infinite,label="ere",position = position_dodge(1))+
geom_jitter(alpha=0.4,size=0.4,position=position_jitterdodge(jitter.width = 0.1,dodge.width = 0.6))+
#geom_point(data=df_plot%>%group_by(variable,Group)%>%summarise(mean=mean(pval)),
# aes(x=variable,y=mean),size=2,color="white",position = position_dodge(1))+
theme_cowplot()+
geom_hline(yintercept = 2,color="red",lty=3)+
theme(axis.text.x = element_text(angle=20,hjust=1))+
ylab(expression(paste("-",log[10],"(",p,".",value,")")))+
scale_fill_manual(values=Methods_color)+
xlab("")+
ggtitle("HVGs (23 genes)")+
theme(legend.title = element_blank(),legend.position = "none",plot.title = element_text(hjust=0.5))
df0_tmp=subset(df_plot,genetype=="LVG" & variable=="Overall (Chisq)" &!Group %in%"Raw")
df0_tmp$Group=factor(df0_tmp$Group,levels = c("CarDEC","Scanorama","DCA","scVI","MNN"))
#df0_tmp%>%group_by(Group)%>%summarise(n=n())
p2=ggplot(df0_tmp,
aes(x=Group,y=pval_log10,fill=Group))+
#geom_violin(aes(x=variable,y=pval),scale = "width",adjust=1)+
geom_boxplot(width=0.5,color="blue",outlier.color = NA,size=0.2,
position = position_dodge(0.6))+
#ggrepel::geom_label_repel(data=df_plot_infinite,label="ere",position = position_dodge(1))+
geom_jitter(alpha=0.4,size=0.4,position=position_jitterdodge(jitter.width = 0.1,dodge.width = 0.6))+
#geom_point(data=df_plot%>%group_by(variable,Group)%>%summarise(mean=mean(pval)),
# aes(x=variable,y=mean),size=2,color="white",position = position_dodge(1))+
theme_cowplot()+
geom_hline(yintercept = 2,color="red",lty=3)+
theme(axis.text.x = element_text(angle=20,hjust=1))+
ylab(expression(paste("-",log[10],"(",p,".",value,")")))+
scale_fill_manual(values=Methods_color)+
xlab("")+
ggtitle("LVGs (38 genes)")+
theme(legend.title = element_blank(),legend.position = "none",plot.title = element_text(hjust=0.5))
#coord_cartesian(ylim=c(0,20))

- the median value for each method in above figure
subset(df_plot, variable=="Overall (Chisq)" &!Group %in%"Raw")%>%
group_by(genetype,Group)%>%
summarise(log_pval_median=median(pval_log10),
logp_mean=mean(pval_log10),
n=n())
Figure 6
width=18
height=20
p6=ggdraw()+draw_plot(p_heatmap,x = 0,y = 1-8/height,height = 8/height,width = 1)+
draw_plot(p_box,x=0,y=0.1/height,height = 11.8/height,width=4.5/width)+
draw_plot(pp0,x=4.5/width,y=0,height = 11.7/height,width = 13.5/width)+
draw_label("a", x=0,y=1,hjust=0,vjust = 1,size=30)+
draw_label("b", x=0.5,y=1,hjust=0,vjust = 1,size=30)+
draw_label("c", x=0,y=12/height,hjust=0,vjust = 1,size=30)+
draw_label("d", x=4.5/width,y=12/height,hjust=0,vjust = 1,size=30)+
draw_label("CarDEC", x=(4.5+13.5/10*1+0.2)/width,y=11.9/height,hjust=0.5,vjust = 1,size=18,fontface = "bold")+
draw_label("Scanorama", x=(4.5+13.5/10*3-0.1)/width,y=11.9/height,hjust=0.5,vjust = 1,size=18,fontface = "bold")+
draw_label("DCA", x=(4.5+13.5/10*5-0.27)/width,y=11.9/height,hjust=0.5,vjust = 1,size=18,fontface = "bold")+
draw_label("scVI", x=(4.5+13.5/10*7-0.5)/width,y=11.9/height,hjust=0.5,vjust = 1,size=18,fontface = "bold")+
draw_label("MNN",x=(4.5+13.5/10*9-0.68)/width,y=11.9/height,hjust=0.5,vjust = 1,size=18,fontface = "bold")
p6

LS0tCnRpdGxlOiAiVGhlIHJlc3VsdHMgZm9yIG1vbm9jeXRlcyBkYXRhc2V0IgpzdWJ0aXRsZTogInJhdyBnZW5lIGV4cHJlc3Npb24gbWF0cml4LCBDYXJERUMsIERDQSwgc2NWSSBhbmQgU2Nhbm9yYW1hIgphdXRob3I6ICBYaWFuZ2ppZSBMaQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclbS8lZC8lWScpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdG9jOiB5ZXMKICBqZWt5bGx0aGF0OjpqZWt5bGxkb3duOgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICAgIHRvYzogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogIHByZXR0eWRvYzo6aHRtbF9wcmV0dHk6CiAgICB0aGVtZTogY2F5bWFuCiAgICBoaWdobGlnaHQ6IGdpdGh1YgogICAgbWF0aDoga2F0ZXgKICAgIHRvYzogeWVzCi0tLQoKPHN0eWxlPgpwcmUgewogIG1heC1oZWlnaHQ6IDIwMHB4OwogIGZsb2F0OiBsZWZ0OwogIHdpZHRoOiA5MTBweDsKICBvdmVyZmxvdy15OiBhdXRvOwp9CnByZS5yIHsKICBtYXgtaGVpZ2h0OiBub25lOwp9Cjwvc3R5bGU+CgotIERhdGEgU3VtbWFyeSAKClRoaXMgZGF0YXNldCB3YXMgZ2VuZXJhdGVkIGJ5IG91ciBncm91cCwgd2hpY2ggY2FuIGJlIGRvd25sb2FkZWQgZnJvbSBbR1NFMTQ2OTc0XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0UxNDY5NzQpIG9yIFtodHRwczovL2RyaXZlLmdvb2dsZS5jb20vZmlsZS9kLzFrUjhIaHVmb28yaDJPdG9tVzhuM2tNMGdhUWhWUzU2NC92aWV3P3VzcD1zaGFyaW5nXShodHRwczovL2RyaXZlLmdvb2dsZS5jb20vZmlsZS9kLzFrUjhIaHVmb28yaDJPdG9tVzhuM2tNMGdhUWhWUzU2NC92aWV3P3VzcD1zaGFyaW5nKS4gVGhpcyBkYXRhc2V0IHdhcyBnZW5lcmF0ZWQgZnJvbSBodW1hbiBwZXJpcGhlcmFsIGJsb29kIG1vbm9udWNsZWFyIGNsZWFyIGNlbGxzIGJ5IEZpY29sbCBTZXBhcmF0aW9uIGZvbGxvd2VkIGJ5IENEMTQgYW5kIENEMTYgcG9zaXRpdmUgY2VsbCBzZWxlY3Rpb24uIFNpbmNlIHRoZSBDRDE0IGFuZCBDRDE2IGFudGlib2RpZXMgYXJlIG5vdCAxMDAlIHNwZWNpZmljLCBzb21lIFQgY2VsbHMgd2VyZSBhbHNvIHByZXNlbnQgaW4gdGhlIHNjUk5BLXNlcSBkYXRhLiBXZSBwZXJmb3JtZWQgY2x1c3RlcmluZyBhbmFseXNpcyB1c2luZyBsZWlkZW7igJlzIGFsZ29yaXRobSBmb3IgZWFjaCBiYXRjaCBhbmQgaWRlbnRpZmllZCAyODggVCBjZWxscyBpbiB0b3RhbCBiYXNlZCBvbiB0aGUgVCBjZWxsIG1hcmtlciBnZW5lcyBDRDNELCBDRDNFIGFuZCBDRDNHLiBBZnRlcmluZyByZW1vdmluZyB0aGVzZSAyODggVCBjZWxscywgdGhlcmUgYXJlIDEwLDg3OCBjZWxscyBhbmQgMjEsMjg5IGdlbmVzLCB3aGljaCB3YXMgcHJvY2Vzc2VkIGFuZCBzZXF1ZW5jZWQgYXQgdGhyZWUgZGlmZmVyZW50IGRheXMsIHJlc3VsdGluZyBpbiB0aHJlZSBiYXRjaGVzICgzLDY0MCBjZWxscyBpbiBUMSwgNCw4MzMgY2VsbHMgaW4gVDIgYW5kIDIsNDA1IGNlbGxzIGluIFQzKSBsZWZ0IGluIHRoZSByZW1haW5pbmcgYW5hbHlzaXMuIAoKX18qKipIdW1hbiBtb25vY3l0ZSBwcmVwYXJhdGlvbioqKl9fOiBNb25vY3l0ZSBwcmVwYXJhdGlvbiB1c2VzIGEgbW9kaWZpY2F0aW9uIG9mIHB1Ymxpc2hlZCBwcm90b2NvbHMuIEJyaWVmbHksIH4yMCBtbCBibG9vZCBkcmF3biBpbiBzb2RpdW0gaGVwYXJpbiB3YXMgcHJvY2Vzc2VkIGltbWVkaWF0ZWx5IGluIHRoZSBsYWIgaW4gdGhlIENsaW5pY2FsIFJlc2VhcmNoIENlbnRlciBhdCBDb2x1bWJpYSBVbml2ZXJzaXR5LiBQQk1DcyB3ZXJlIGlzb2xhdGVkIGJ5IGdyYWRpZW50IEZpY29sbCBwYXF1ZSBjZW50cmlmdWdhdGlvbiwgd2hpY2ggbWFpbnRhaW5zIGNlbGwgdmlhYmlsaXR5IGFuZCBwcmV2ZW50cyBleCB2aXZvIGFjdGl2YXRpb24gZHVyaW5nIGNlbGwgcmVjb3ZlcnkuIENlbGxzIHdlcmUgc3RhaW5lZCB3aXRoIGFudGlib2RpZXMgYWdhaW5zdCBodW1hbiBITEFEUiwgQ0QxNCBhbmQgQ0QxNiBhbmQgbW9ub2N5dGUgc3Vic2V0cyBkZWZpbmVkIGFzIEhMQURSK0NEMTQrK0NEMTYtKGNsYXNzaWNhbCksIEhMQURSK0NEMTQrK0NEMTYrIChpbnRlcm1lZGlhdGUpLCBITEFEUitDRDE0ZGltL0NEMTYrKyAobm9uY2xhc3NpY2FsLCBwYXRyb2xsaW5nIG1vbm9jeXRlKS4gREFQSSBzdGFpbmluZyB3YXMgdXNlZCB0byBleGNsdWRlIGRlYWQgY2VsbHMuIE1vbm9jeXRlcyB3ZXJlIHNvcnRlZCBieSBhIEJEIEluZmx1eCBTb3J0ZXIgaW50byB0dWJlcyBmb3IgcmVhbC10aW1lIDEweCBHZW5vbWljcyBhbmFseXNpcy4KCgpgYGB7cn0Kb3B0aW9ucyh3YXJuPS0xKSAjIHR1cm4gb2ZmIHdhcm5pbmcgbWVzc2FnZSBnbG9iYWxseQoubGliUGF0aHMoYygiL2hvbWUveGlhb3hpYW5nL1IveDg2XzY0LXBjLWxpbnV4LWdudS1saWJyYXJ5LzMuNSIsLmxpYlBhdGhzKCkpKQpTeXMuc2V0ZW52KFJFVElDVUxBVEVfUFlUSE9OX0VOVj0iL2hvbWUveGlhb3hpYW5nL2FuYWNvbmRhMy9lbnZzL2NhcmRlYyIpIz0iL2hvbWUveGlhb3hpYW5nLy5jb25kYS9lbnZzL0RFU0NWSVIiCgojU3lzLnNldGVudihSRVRJQ1VMQVRFX1BZVEhPTj0iL3Vzci9iaW4vcHl0aG9uMyIpCiNSRVRJQ1VMQVRFX1BZVEhPTj0iL2hvbWUveGlhb3hpYW5nL2FuYWNvbmRhMy9iaW4vcHl0aG9uMyIsCmlmICgiU2V1cmF0IiAlaW4lIGxvYWRlZE5hbWVzcGFjZXMoKSkgZGV0YWNoKCJwYWNrYWdlOlNldXJhdCIsdW5sb2FkID0gVCkKZHluLmxvYWQoIi9ob21lL3hpYW94aWFuZy9SL3g4Nl82NC1wYy1saW51eC1nbnUtbGlicmFyeS8zLjUvc2YvbGlicy9zZi5zbyIpCiNzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShtb25vY2xlLGxpYi5sb2MgPSAiL3Vzci9saWIvUi9tb25vY2xlX2FscGhhIikpIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIiIpCiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoImNvbGUtdHJhcG5lbGwtbGFiL0REUlRyZWUiLCByZWY9InNpbXBsZS1wcHQtbGlrZSIsbGliPSIvdXNyL2xpYi9SL21vbm9jbGVfYWxwaGEiKQojZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJyLXNwYXRpYWwvc2YiKSBpZiAKI2luc3RhbGwucGFja2FnZXMoIn4vRG93bmxvYWRzL21vbm9jbGUtcmVsZWFzZS1tb25vY2xlM19hbHBoYS8iLCByZXBvcyA9IE5VTEwsbGliID0gIi91c3IvbGliL1IvbW9ub2NsZV9hbHBoYSIpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KHJldGljdWxhdGUpKQp1c2VfY29uZGFlbnYoImNhcmRlYyIpCgojc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoZGV2dG9vbHMpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShtb25vY2xlKSkKCiNzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShmbGV4Y2x1c3QpKQojc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkobWNjbHVzdCkpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KGRwbHlyKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoZ2dqb3kpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShWR0FNKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoa25pdHIpKQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShnZ3Bsb3QyKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoa2FibGVFeHRyYSkpCnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKSkKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoY293cGxvdCkpCiNweV9pbnN0YWxsKCd1bWFwLWxlYXJuJywgcGlwID0gVCwgcGlwX2lnbm9yZV9pbnN0YWxsZWQgPSBUKQojaW1wb3J0KCJsZWlkZW4iKQojZmlnX3BhdGg9Ii9ob21lL3hpYW94aWFuZy9Eb2N1bWVudHMvREVTQ19wYXBlcl9wcmVwYXJlL0RFU0NfcGFwZXJfZmluYWwvZm9ybWFsX3JldmlzZWQvZmlndXJlc19zZXAvIgpkYXRhZGlycGF0aD0iLi8iCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvPVQpCmBgYAoKYGBge3J9CmlmKFIudmVyc2lvbiRvcz09ImxpbnV4LWdudSIpewogICNpbiBteSB1YnVudHUgY29tcHV0ZXIKICBzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgiU2V1cmF0IixsaWIubG9jID0gIi91c3IvbGliL1Ivc2VsZl9saWJyYXJ5LyIpKQogIHN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KCJTZXVyYXRXcmFwcGVycyIsbGliLmxvYyA9ICIvdXNyL2xpYi9SL3NlbGZfbGlicmFyeS8iKSkKfWVsc2V7CiAgI2luIG15IG1hY2Jvb2sKICBzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeShTZXVyYXQpKQogIHN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KFNldXJhdFdyYXBwZXJzKSkKfQpgYGAKCgpgYGB7cn0KIyBsb2FkIG5lY2Vzc2F5IGZ1bmN0aW9uCiNzb3VyY2UoIi9tZWRpYS94aWFveGlhbmcvRC9ERVNDX3JlcHJvZHVjaWJsZV9maWxlL2hlbHBmdW5jX25ldy5SIikKI3NvdXJjZSgiL21lZGlhL3hpYW94aWFuZy9EL1VwZW5uX2NvbXB1dGVyX2JhY2t1cC9Eb2N1bWVudHMvSHVtYW5fSGVhcnRfUHJvamVjdC9oZWFydC9IZWFydF9yZXN1bHRfdXBkYXRlZC9oZWxwZnVuY19uZXcuUiIpCm9sZD10aGVtZV9zZXQodGhlbWVfYncoKSt0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9IndoaXRlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmdyaWQgPWVsZW1lbnRfYmxhbmsoKSkpCgpCYXRjaEtMPWZ1bmN0aW9uKGRmLGRpbWVuc2lvbkRhdGE9TlVMTCxyZXBsaWNhdGVzPTIwMCxuX25laWdoYm9ycz0xMDAsbl9jZWxscz0xMDAsYmF0Y2g9IkJhdGNoSUQiKXsKICAjZW50cm9weSBvZiBiYXRjaCBtaXhpaW5nCiAgI3JlcGxpY2F0ZXMgaXMgdGhlIG51bWJlciBvZiBib29zdHJhcCB0aW1lcwogICNuX25laWdoYm9ycyBpcyB0aGUgbnVtYmVyIG9mIG5lYXJlc3QgbmVpZ2hib3VycyBvZiBjZWxsKGZyb20gYWxsIGJhdGNocykKICAjbl9jZWxscyBpcyB0aGUgbnVtYmVyIG9mIHJhbmRvbWx5IHBpY2tlZCBjZWxscwogIGlmIChpcy5udWxsKGRpbWVuc2lvbkRhdGEpKXsKICAgICAgICB0c25lZGF0YT1hcy5tYXRyaXgoZGZbLGMoInRTTkVfMSIsInRTTkVfMiIpXSkKICB9ZWxzZXsKICAgICAgICB0c25lZGF0YT1hcy5tYXRyaXgoZGltZW5zaW9uRGF0YSkKICB9CiAgYmF0Y2hkYXRhPWZhY3Rvcihhcy52ZWN0b3IoZGZbLGJhdGNoXSkpCiAgdGFibGUuYmF0Y2hkYXRhPWFzLm1hdHJpeCh0YWJsZShiYXRjaGRhdGEpKVssMV0KICB0bXAwMD10YWJsZS5iYXRjaGRhdGEvc3VtKHRhYmxlLmJhdGNoZGF0YSkjcHJvcG9ydGF0aW9uIG9mIHBvcHVsYXRpb24KICBuPWRpbShkZilbMV0KICBLTD1zYXBwbHkoMTpyZXBsaWNhdGVzLGZ1bmN0aW9uKHgpewogICAgYm9vdHNhbXBsZXM9c2FtcGxlKDE6bixuX2NlbGxzKQogICAgI25lYXJlc3Q9bm4yKHRzbmVkYXRhLHRzbmVkYXRhW2Jvb3RzYW1wbGVzLF0saz1uX25laWdoYm9ycykKICAgIG5lYXJlc3Q9bmFib3I6Omtubih0c25lZGF0YSx0c25lZGF0YVtib290c2FtcGxlcyxdLGs9bWluKDUqbGVuZ3RoKHRtcDAwKSxuX25laWdoYm9ycykpCiAgICBLTF94PXNhcHBseSgxOmxlbmd0aChib290c2FtcGxlcyksZnVuY3Rpb24oeSl7CiAgICAgIGlkPW5lYXJlc3Qkbm4uaWR4W3ksXQogICAgICB0bXA9YXMubWF0cml4KHRhYmxlKGJhdGNoZGF0YVtpZF0pKVssMV0KICAgICAgdG1wPXRtcC9zdW0odG1wKQogICAgICByZXR1cm4oc3VtKHRtcCpsb2cyKHRtcC90bXAwMCksbmEucm0gPSBUKSkKICAgIH0pCiAgICByZXR1cm4obWVhbihLTF94LG5hLnJtID0gVCkpCiAgfSkKICByZXR1cm4oS0wpCn0KYGBgCgpgYGB7cn0KQ29udmVydF90b19zZXVyYXQzPWZ1bmN0aW9uKGFkYXRhKXsKICBzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMobGlicmFyeSgiU2V1cmF0IixsaWIubG9jID0gIi91c3IvbGliL1Ivc2VsZl9saWJyYXJ5LyIpKQogIG10eD1weV90b19yKGFkYXRhJFgkVCR0b2NzYygpKQogIGNlbGxpbmZvPXB5X3RvX3IoYWRhdGEkb2JzKQogIGdlbmVpbmZvPXB5X3RvX3IoYWRhdGEkdmFyKQogIGNvbG5hbWVzKG10eCk9Y2VsbGluZm8kY2VsbG5hbWUKICByb3duYW1lcyhtdHgpPXJvd25hbWVzKGdlbmVpbmZvKQogIG9iaj1DcmVhdGVTZXVyYXRPYmplY3QobXR4LG1ldGEuZGF0YSA9IGNlbGxpbmZvWywhY29sbmFtZXMoY2VsbGluZm8pJWluJWMoIm5fZ2VuZXMiLCJuX2NvdW50cyIpLGRyb3A9Rl0sbWluLmZlYXR1cmVzICA9IDEpCiAgcmV0dXJuKG9iaikKfQpnZXR3ZCgpCmBgYAoKIyByZWFkaW5nIHJhdyBkYXRhIAoKYGBge3J9CmFkPWltcG9ydCgiYW5uZGF0YSIsY29udmVydCA9IEZBTFNFKQphZGF0YT1hZCRyZWFkX2g1YWQoIi4uLy4uL2RjYV90ZXN0Lmg1YWQiKSNkY2FfdGVzdC5oNWFkIGlzIHRoZSBtb25vY3l0ZSBkYXRhIHVzZWQgZm9yIENhckRFQwpvYmowPUNvbnZlcnRfdG9fc2V1cmF0MyhhZGF0YSkKb2JqMD1Ob3JtYWxpemVEYXRhKG9iajAsdmVyYm9zZSA9IEYpCnJhdy5kYXRhPW9iajBAYXNzYXlzJFJOQUBjb3VudHMKYGBgCgpgYGB7cn0KbWFwcnVsZXM9YygiMjAxN18wODAxIj0iVDEiLCIyMDE3XzEwMTciPSJUMiIsIjIwMTdfMTEyMCI9IlQzIikKbWFwcnVsZXMKYGBgCgojIHJlYWRpbmcgcHJvY2Vzc2VkIGRhdGEgYnkgQ2FyREVDCgpgYGB7cn0KYWRhdGE9YWQkcmVhZF9oNWFkKCIuLi9DYXJERUMgUmVzdWx0cy9hZGF0YV9DYXJERUMuaDVhZCIpCmBgYAoKCmBgYHtyfQpjZWxsLm1ldGEuZGF0YT1weV90b19yKGFkYXRhJG9icykKY2VsbC5tZXRhLmRhdGEkZGF0YXNldF9iYXRjaD1wbHlyOjptYXB2YWx1ZXMoY2VsbC5tZXRhLmRhdGEkYmF0Y2hfbGFiZWwsbmFtZXMobWFwcnVsZXMpLG1hcHJ1bGVzKQpnZW5lX2FubjA9cHlfdG9fcihhZGF0YSR2YXIpCgptdHg9dChweV90b19yKGFkYXRhJGxheWVyc1snZGVub2lzZWQgY291bnRzJ10pKQpjb2xuYW1lcyhtdHgpPWNlbGwubWV0YS5kYXRhJGNlbGxuYW1lCnJvd25hbWVzKG10eCk9cm93bmFtZXMoZ2VuZV9hbm4wKQptdHhfc2l6ZWZhY3Rvcj0xZTQvY29sU3VtcyhtdHgpCmBgYAoKYGBge3J9Cm9iajE9Q3JlYXRlU2V1cmF0T2JqZWN0KG10eCxtZXRhLmRhdGEgPSBjZWxsLm1ldGEuZGF0YSkKb2JqMT1Ob3JtYWxpemVEYXRhKG9iajEsdmVyYm9zZSA9IEYpCklkZW50cyhvYmoxKT0iQmF0Y2hJRCIgI29iajEgbWVhbnMgZGVub2lzZWQgY291bnQgYnkgQ2FyREVDCmBgYAoKYGBge3J9CmF2Z19leHA9U2V1cmF0Ojo6RmFzdEV4cE1lYW4ob2JqMUBhc3NheXMkUk5BQGNvdW50cyxkaXNwbGF5X3Byb2dyZXNzID0gRikjbG9nMXAoCmdlbmVfYW5uX2FsbD1kYXRhLmZyYW1lKGdlbmVfc2hvcnRfbmFtZSA9IG1ha2UudW5pcXVlKHJvd25hbWVzKGdlbmVfYW5uMCkpLAogICAgICAgICAgICAgICAgICAgIFZhcmlhbmNlVHlwZT1nZW5lX2FubjAkYFZhcmlhbmNlIFR5cGVgLAogICAgICAgICAgICAgICAgICAgIGF2Z19leHA9YXZnX2V4cCwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSBtYWtlLnVuaXF1ZShyb3duYW1lcyhnZW5lX2FubjApKSkKYGBgCgoKCmBgYHtyfQojYXNzaWduIHBzZXVkb3RpbWUgZm9yIG9iajAKY2RzPXJlYWRSRFMoIi4vY2RzX2NhcmRlYy5yZHMiKSAjY3NkX2NhcmRlYyBpcyB0aGUgbW9ub2NsZSdzIG91dHB1dCBmcm9tIENhckRFQwp4dG1wPWNvbERhdGEoY2RzKVssYygiUHNldWRvdGltZSIpXSU+JWFzLmRhdGEuZnJhbWUoKQpvYmowJFBzZXVkb3RpbWU9Y29sRGF0YShjZHMpJFBzZXVkb3RpbWUKb2JqMSRQc2V1ZG90aW1lPWNvbERhdGEoY2RzKSRQc2V1ZG90aW1lCmBgYAoKCmBgYHtyfQpvYmowPU5vcm1hbGl6ZURhdGEob2JqMCx2ZXJib3NlID0gRikKb2JqMD1GaW5kVmFyaWFibGVGZWF0dXJlcyhvYmowLHZlcmJvc2UgPSBGKQp4eD1vYmowQGFzc2F5cyRSTkFAbWV0YS5mZWF0dXJlc1ssYygidnN0Lm1lYW4iKSxkcm9wPUZdCnh4JGdlbmU9cm93bmFtZXMoeHgpCmBgYAoKV2Ugb2J0YWluZWQgVEZzIHJlbGF0ZWQgdG8gbW9ub2N5dGVzIGZyb20gRmlndXJlIDQgaW4gW0dlbmUgZXhwcmVzc2lvbiBwcm9maWxpbmcgcmV2ZWFscyB0aGUgZGVmaW5pbmcgZmVhdHVyZXMgb2YgdGhlIGNsYXNzaWNhbCwgaW50ZXJtZWRpYXRlLCBhbmQgbm9uY2xhc3NpY2FsIGh1bWFuIG1vbm9jeXRlIHN1YnNldHNdKGh0dHBzOi8vYXNocHVibGljYXRpb25zLm9yZy9ibG9vZC9hcnRpY2xlLzExOC81L2UxNi8yOTAxNi9HZW5lLWV4cHJlc3Npb24tcHJvZmlsaW5nLXJldmVhbHMtdGhlLWRlZmluaW5nKSAKCmBgYHtyfQp0Zl9kZj1vcGVueGxzeDo6cmVhZC54bHN4KCIuL1RGX2Jsb29kLnhsc3giKSU+JWZpbHRlcihHZW5lJWluJXJvd25hbWVzKG9iajApKSU+JQogIGxlZnRfam9pbihnZW5lX2Fubl9hbGwsYnk9YygiR2VuZSI9ImdlbmVfc2hvcnRfbmFtZSIpKSU+JQogIGxlZnRfam9pbih4eCxieT1jKCJHZW5lIj0iZ2VuZSIpKSU+JQogIGFzLmRhdGEuZnJhbWUoKQpyb3duYW1lcyh0Zl9kZik9dGZfZGYkR2VuZQp0Zl9kZgpgYGAKCgpgYGB7cn0KbnVtX2NlbGxzX2V4cHJlc3NlZD1NYXRyaXg6OnJvd1N1bXMob2JqMEBhc3NheXMkUk5BQGNvdW50c1t0Zl9kZiRHZW5lLF0hPTApCnRmX2RmJG51bV9jZWxsc19leHByZXNzZWQ9bnVtX2NlbGxzX2V4cHJlc3NlZFtyb3duYW1lcyh0Zl9kZildCmBgYAoKCiMgSGVhdG1hcC1ubyBzbW9vdGhlZAoKKipOb3RlOioqIGdlbmVzIHdpdGggY29sb3IgPHNwYW4gc3R5bGU9ImNvbG9yOiMzNzdFQjgiPiAgYmx1ZSA8L3NwYW4+ICBtZWFucyBMVkdzIGFuZCBnZW5lcyB3aXRoIDxzcGFuIHN0eWxlPSJjb2xvcjojRTQxQTFDIj4gcmVkIDwvc3Bhbj4gbWVhbnMgSFZHcy4gV2UgZmlyc3RseSBmaWx0ZXJlZCBvdXQgZ2VuZXMgd2l0aCBhdmVyYWdlIGV4cHJlc3Npb24gbG93ZXIgdGhhbiAwLjEgYW5kIHNvcnRlZCB0aGUgZ2VuZXMgZGVjcmVhc2luZ2x5IGJ5IGF2ZXJhZ2UgZXhwcmVzc2lvbiBncm91cGVkIGJ5IGluIGVhY2ggZ2VuZSBjbGFzcyAoSSBzZXQgMyBjbHVzdGVycyBmb3IgZ2VuZSwgYnV0IGRpZG4ndCBzaG93IGhlcmUpIGFuZCBoaWdobGlnaHRlZCB0aGUgdG9wIDE1IGdlbmVzLiAKCiMjIHBzZXVkb3RpbWUgZnJvbSBjYXJkZWMncyBkZW5vaXNlZCBnZW5lIGV4cHJlc3Npb24KCmBgYHtyfQpvYmoxPU5vcm1hbGl6ZURhdGEob2JqMSx2ZXJib3NlID0gRikKb2JqMT1GaW5kVmFyaWFibGVGZWF0dXJlcyhvYmoxLHZlcmJvc2UgPSBGKQpodmYuaW5mbz1vYmoxQGFzc2F5cyRSTkFAbWV0YS5mZWF0dXJlcwpnZW5lMjI9aHZmLmluZm9bb3JkZXIoaHZmLmluZm8kdnN0LnZhcmlhbmNlLnN0YW5kYXJkaXplZCwgIGRlY3JlYXNpbmcgPSBUUlVFKSwgLGRyb3AgPSBGQUxTRV0KbnVtX2NlbGxzX2V4cHJlc3NlZD1NYXRyaXg6OnJvd1N1bXMob2JqMUBhc3NheXMkUk5BQGNvdW50c1t0Zl9kZiRHZW5lLF0hPTApCm9iajE9U2NhbGVEYXRhKG9iajEsZmVhdHVyZXMgPSB0Zl9kZiRHZW5lLHZlcmJvc2UgPSBGKQpgYGAKCgoKYGBge3J9CiNzY2FsZWQKI20gPSB0KHNjYWxlKG0xLGNlbnRlciA9IFQpKQptPXQoRmV0Y2hEYXRhKG9iajEsdmFycyA9IHRmX2RmJEdlbmUsc2xvdCA9ICJzY2FsZS5kYXRhIikpCm1baXMubmFuKG0pXSA9IDAKbVttID49IDNdID0gMwptW20gPD0gLTNdID0gLTMKaGVhdG1hcF9tYXRyaXggPC0gbQpyb3dfZGlzdCA8LSBhcy5kaXN0KCgxIC0gY29yKHQoaGVhdG1hcF9tYXRyaXgpKSkvMikKcmVzMiA8LSBsaXN0KHBoPXBoZWF0bWFwOjpwaGVhdG1hcChoZWF0bWFwX21hdHJpeCwgdXNlUmFzdGVyID0gVCwgY2x1c3Rlcl9jb2xzID0gRkFMU0UsIAogICAgICAgIGNsdXN0ZXJfcm93cyA9IFQsIHNob3dfcm93bmFtZXMgPSBGLCBzaG93X2NvbG5hbWVzID0gRiwgCiAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gcm93X2Rpc3QsIGNsdXN0ZXJpbmdfbWV0aG9kID0gICJ3YXJkLkQyIiwgCiAgICAgICAgY3V0cmVlX3Jvd3MgPSAzLCBzaWxlbnQgPSBUUlVFLCBmaWxlbmFtZSA9IE5BKSxtPWhlYXRtYXBfbWF0cml4KQogCmBgYAoKCmBgYHtyfQp6PXJlczJbWzJdXVtyZXMyW1sxXV0kdHJlZV9yb3ckb3JkZXIsXQpDbHVzdGVyX2xhYmVscz1jdXRyZWUocmVzMltbMV1dJHRyZWVfcm93LDMpW3JlczJbWzFdXSR0cmVlX3JvdyRvcmRlcl0Kcm93X2Fubm89ZGF0YS5mcmFtZShnZW5lbmFtZT1yb3duYW1lcyh6KSwKICAgICAgICAgICAgICAgICAgICBDbHVzdGVyPUNsdXN0ZXJfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgIENsYXNzPXRmX2RmW3Jvd25hbWVzKHopLCJHZW5lIl0sCiAgICAgICAgICAgICAgICAgICAgVmFyaWFuY2VUeXBlPXRmX2RmW3Jvd25hbWVzKHopLCJWYXJpYW5jZVR5cGUiXSwKICAgICAgICAgICAgICAgICAgICBHZW5lVHlwZT10Zl9kZltyb3duYW1lcyh6KSwiR2VuZVR5cGUiXSwKICAgICAgICAgICAgICAgICAgICAjYXZnX2V4cD10Zl9kZltyb3duYW1lcyh6KSwiYXZnX2V4cCJdLAogICAgICAgICAgICAgICAgICAgIHZzdC5tZWFuPWdlbmUyMltyb3duYW1lcyh6KSwidnN0Lm1lYW4iXSwKICAgICAgICAgICAgICAgICAgICB2c3QudmFyaWFuY2Uuc3RhbmRhcmQ9Z2VuZTIyW3Jvd25hbWVzKHopLCJ2c3QudmFyaWFuY2Uuc3RhbmRhcmRpemVkIl0sCiAgICAgICAgICAgICAgICAgICAgbnVtX2NlbGxzX2V4cHJlc3NlZD1udW1fY2VsbHNfZXhwcmVzc2VkW3Jvd25hbWVzKHopXSwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSByb3duYW1lcyh6KSxzdHJpbmdzQXNGYWN0b3JzID0gRikKcm93X2Fubm8kR2VuZUNsYXNzPXBseXI6Om1hcHZhbHVlcyhyb3dfYW5ubyRDbHVzdGVyLGZyb209c29ydCh1bmlxdWUocm93X2Fubm8kQ2x1c3RlcikpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvPXBhc3RlMCgiTW9kdWxlIixhcy5udW1lcmljKGZhY3Rvcihzb3J0KHVuaXF1ZShDbHVzdGVyX2xhYmVscykpKSkpKQoKI29yZGVyX2lkMD1vcmRlcihyb3dfYW5ubyRDbHVzdGVyLHJvd19hbm5vJEdlbmVUeXBlLHJvd19hbm5vJFZhcmlhbmNlVHlwZSkKI3o9eltvcmRlcl9pZDAsXQojcm93X2Fubm89cm93X2Fubm9bb3JkZXJfaWQwLF0KI0dlbmVDbGFzcy5jb2xvcj1nZ19jb2xvcl9odWUobGVuZ3RoKHVuaXF1ZShDbHVzdGVyX2xhYmVscykpKQpHZW5lQ2xhc3MuY29sb3I9UkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDMsIkRhcmsyIikKbmFtZXMoR2VuZUNsYXNzLmNvbG9yKT1wYXN0ZTAoIk1vZHVsZSIsYXMubnVtZXJpYyhmYWN0b3Ioc29ydCh1bmlxdWUoQ2x1c3Rlcl9sYWJlbHMpKSkpKQojIGdlbmUgTW9kdWxlIGNvbG9yIApyb3dfYW5ubyRjb2xvcj1wbHlyOjptYXB2YWx1ZXMocm93X2Fubm8kR2VuZUNsYXNzLGZyb209bmFtZXMoR2VuZUNsYXNzLmNvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0bz1HZW5lQ2xhc3MuY29sb3IpCnJvd19hbm5vJEhWR19jb2xvcj1wbHlyOjptYXB2YWx1ZXMoYXMuY2hhcmFjdGVyKHJvd19hbm5vJFZhcmlhbmNlVHlwZSksZnJvbT1jKCJIVkciLCJMVkciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0bz1jKCIjRTQxQTFDIiwiIzM3N0VCOCIpKQpgYGAKCmBgYHtyfQpyb3duYW1lcyhyb3dfYW5ubyk9cm93X2Fubm8kZ2VuZW5hbWUKcHNldWRvdGltZV9jb2xfZnVuID1jaXJjbGl6ZTo6Y29sb3JSYW1wMihzZXEoMCwgMSxsZW5ndGg9MTUwKSwgY29sb3JSYW1wUGFsZXR0ZShtb25vY2xlOjo6Ymx1ZTJncmVlbjJyZWQoMTUwKSkoMTUwKSkKIyBjb2x1bWFuIGFubm90YXRpb24KI2NvbF9hbm5vPWRhdGEuZnJhbWUocHNldWRvdGltZT1zZXEoMCwxLGxlbmd0aD1uY29sKHopKSwKIyAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gYXMuY2hhcmFjdGVyKDE6bmNvbCh6KSkpCmNvbF9hbm5vPWRhdGEuZnJhbWUoQmF0Y2hJRD1wbHlyOjptYXB2YWx1ZXMob2JqMSRCYXRjaElELGMoIk1IMDAxIiwiUlAwMDIiLCJSUDAwOSIpLGMoIlQxIiwiVDIiLCJUMyIpKSwKICAgICAgICAgICAgICAgICAgICBQc2V1ZG90aW1lPW9iajEkUHNldWRvdGltZSwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSBjb2xuYW1lcyhvYmoxKSkKY29sX2lkPW9yZGVyKGNvbF9hbm5vJFBzZXVkb3RpbWUpCno9elssY29sX2lkXQpjb2xfYW5ubz1jb2xfYW5ub1tjb2xfaWQsXQojIApzZXQuc2VlZCgxMCkKCnJvd19hbm5vLnRtcD1yb3dfYW5ubyU+JQogIGZpbHRlcih2c3QubWVhbj4wLjEpJT4lCiAgZ3JvdXBfYnkoVmFyaWFuY2VUeXBlKSU+JQogIGFycmFuZ2UoZGVzYyh2c3QubWVhbiksLmJ5X2dyb3VwID0gVFJVRSklPiUKICBtdXRhdGUobl9vcmRlcj0xOm4oKSklPiVhcy5kYXRhLmZyYW1lKCkKCiNpZC5zZWxlY3Q9b3JkZXIocm93X2Fubm8kcXZhbCxkZWNyZWFzaW5nID0gRilbMTozMF0KaWQuc2VsZWN0PXdoaWNoKHJvd19hbm5vJGdlbmVuYW1lJWluJXJvd19hbm5vLnRtcCRnZW5lbmFtZVtyb3dfYW5uby50bXAkbl9vcmRlciVpbiVjKDE6MjApXSkKcm93X2Fubm8ubmV3PXJvd19hbm5vW2lkLnNlbGVjdCxdCmNvbF9mdW4gPSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihzZXEoLTMsIDMsbGVuZ3RoPTIwMCksIGNvbG9yUmFtcFBhbGV0dGUoYygiYmx1ZSIsICJ3aGl0ZSIsICJyZWQiKSkoMjAwKSkKI3BzZXVkb3RpbWVfY29sX2Z1biA9Y2lyY2xpemU6OmNvbG9yUmFtcDIoc2VxKDAsIDEsbGVuZ3RoPTE1MCksIGNvbG9yUmFtcFBhbGV0dGUoYygiZGFya2JsdWUiLCJibGFjayIsIiNGRkZGMDAiKSkoMTUwKSkKcHNldWRvdGltZV9jb2xfZnVuID1jaXJjbGl6ZTo6Y29sb3JSYW1wMihzZXEoMCwgMSxsZW5ndGg9MTUwKSwgY29sb3JSYW1wUGFsZXR0ZShtb25vY2xlOjo6Ymx1ZTJncmVlbjJyZWQoMTUwKSkoMTUwKSkKdG9wX2Fubm89SGVhdG1hcEFubm90YXRpb24oIFBzZXVkb3RpbWUgPSBhbm5vX3NpbXBsZShjb2xfYW5ubyRQc2V1ZG90aW1lLCBjb2wgPSBwc2V1ZG90aW1lX2NvbF9mdW4pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgQmF0Y2hJRD1jb2xfYW5ubyRCYXRjaElELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbD1saXN0KEJhdGNoSUQ9YygiVDEiPSIjNjZDMkE1IiwiVDIiPSIjRkM4RDYyIiwiVDMiPSIjOERBMENCIikpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfbGVnZW5kID0gYyhULFQpLAogICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fbmFtZV9zaWRlID0gInJpZ2h0IikKCmxlZnRfYW5ubz1yb3dBbm5vdGF0aW9uKCNHZW5lQ2xhc3M9cm93X2Fubm8kR2VuZUNsYXNzLAogICAgICAgICAgICAgICAgICAgICAgICAjR2VuZVR5cGU9cm93X2Fubm8kR2VuZVR5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICBWYXJpYW5jZVR5cGU9cm93X2Fubm8kVmFyaWFuY2VUeXBlLAogICAgICAgICAgICAgICAgICAgICAgI05vbnplcm89YW5ub19iYXJwbG90KGxvZzEwKHJvd19hbm5vJG51bV9jZWxsc19leHByZXNzZWQpLAogICAgICAgICAgICAgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdwICA9IGdwYXIoZmlsbCA9IiMwMEM0RkYiLGNvbD1OQSksCiAgICAgICAgICAgICAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFyX3dpZHRoID0gMSwKICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQgPSB1bml0KDIuNSwgImNtIikpLAogICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fbmFtZV9yb3Q9OTAsCiAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX25hbWVfc2lkZSA9ICJ0b3AiLAogICAgICAgICAgICAgICAgICAgICAgICAjY29sPWxpc3QoR2VuZUNsYXNzPWNlbGx0eXBlX2NvbG9yW25hbWVzKGNlbGx0eXBlX2NvbG9yKSVpbiV1bmlxdWUodG1wLmFubm8kY2VsbHR5cGUpXSksCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbD1saXN0KEdlbmVDbGFzcz1HZW5lQ2xhc3MuY29sb3IsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZhcmlhbmNlVHlwZT1jKCJIVkciPSIjRTQxQTFDIiwiTFZHIj0iIzM3N0VCOCIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19hbm5vdGF0aW9uX25hbWU9YyhGLFQpLAogICAgICAgICAgICAgICAgICAgICAgICBzaG93X2xlZ2VuZD1jKFQsVCkpCnAxPUNvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKHosIG5hbWUgPSAic2NhbGVkLmV4cHJlc3Npb24iLCAKICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfcm93cyA9IEYsCiAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBGLAogICAgICAgICAgICAgICAgICAgICAgY29sPWNvbF9mdW4sCiAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbGFiZWxzID0gIHJlcCgiIixsZW5ndGg9bmNvbCh6KSksCiAgICAgICAgICAgICAgICAgICAgICByb3dfbGFiZWxzID0gcmVwKCIiLGxlbmd0aD1ucm93KHopKSwKICAgICAgICAgICAgICAgICAgICAgICNyb3dfbGFiZWxzID0gZ2VuZS51c2UuZGYkZ2VuZSwKICAgICAgICAgICAgICAgICAgICAgIHJvd19uYW1lc19zaWRlID0gInJpZ2h0IiwKICAgICAgICAgICAgICAgICAgICAgICN0aGUgZm9sbG93aW5nIGZvbnRzaXplIGlzIG5vbnVzZWZ1bGwgZm9yIHRoaXMgc2l0dWF0dGlvbgogICAgICAgICAgICAgICAgICAgICAgcm93X25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZT01KSwKICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9uYW1lc19ncCA9IGdwYXIoZm9udHNpemUgPSA4KSwKICAgICAgICAgICAgICAgICAgICAgICNjb2x1bW5fZ2FwID0gdW5pdCgwLjUsICJtbSIpLAogICAgICAgICAgICAgICAgICAgICAgI2NvbHVtbl9zcGxpdCA9IHRtcC5hbm5vJHJlcywKICAgICAgICAgICAgICAgICAgICAgICNjb2x1bW5fdGl0bGUgPSAiJXMiLAogICAgICAgICAgICAgICAgICAgICAgI2NvbHVtbl90aXRsZV9yb3Q9OTAsCiAgICAgICAgICAgICAgICAgICAgICAjcm93X2dhcCA9IHVuaXQoMC41LCAibW0iKSwKICAgICAgICAgICAgICAgICAgICAgICNyb3dfc3BsaXQgPSByb3dfYW5ubyRHZW5lQ2xhc3MsCiAgICAgICAgICAgICAgICAgICAgICAjcm93X3RpdGxlID0gIiVzIiwKICAgICAgICAgICAgICAgICAgICAgIHJvd190aXRsZV9ncCA9IGdwYXIoZm9udHNpemUgPSAxMiksCiAgICAgICAgICAgICAgICAgICAgICByb3dfdGl0bGVfcm90ID0gOTAsCiAgICAgICAgICAgICAgICAgICAgICB0b3BfYW5ub3RhdGlvbiA9IHRvcF9hbm5vLAogICAgICAgICAgICAgICAgICAgICAgbGVmdF9hbm5vdGF0aW9uPSBsZWZ0X2Fubm8sCiAgICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfcGFyYW09bGlzdChsZWdlbmRfZGlyZWN0aW9uPSJob3Jpem9udGFsIixsZWdlbmRfd2lkdGggPSB1bml0KDIuNSwgImNtIikpLAogICAgICAgICAgICAgICAgICAgICAgcmlnaHRfYW5ub3RhdGlvbiA9IHJvd0Fubm90YXRpb24oZm9vPWFubm9fbWFyayhhdD1pZC5zZWxlY3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZGU9InJpZ2h0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZXh0ZW5kPXVuaXQoMC4yLCJjbSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHNfZ3AgPSBncGFyKGZvbnRzaXplPTgsY29sPXJvd19hbm5vJEhWR19jb2xvcltpZC5zZWxlY3RdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPXJvd19hbm5vLm5ldyRnZW5lbmFtZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX25hbWVfc2lkZT0idG9wIikpCmBgYAoKCmBgYHtyLGZpZy53aWR0aD05LGZpZy5oZWlnaHQ9OH0KI2RyYXcocDEscGFkZGluZz11bml0KGMoMiwyLDIsMiksIm1tIikpCmxnZF9wc2U9TGVnZW5kKHRpdGxlID0gIlBzZXVkb3RpbWUiLCBjb2wgPSBwc2V1ZG90aW1lX2NvbF9mdW4sIGF0ID0gYygwLDAuNSwxKSwgCiAgICBsYWJlbHMgPSBjKCJsb3ciLCJtZWQiLCJoaWdoIiksbGVnZW5kX2hlaWdodCA9IHVuaXQoMi41LCAiY20iKSxib3JkZXIgPSBOQSx0aXRsZV9wb3NpdGlvbiA9ICJ0b3BjZW50ZXIiLGRpcmVjdGlvbj0iaG9yaXpvbnRhbCIpCmRyYXcocDEsYW5ub3RhdGlvbl9sZWdlbmRfbGlzdCA9IGxpc3QobGdkX3BzZSksbWVyZ2VfbGVnZW5kcz1ULGhlYXRtYXBfbGVnZW5kX3NpZGU9ImJvdHRvbSIpCmBgYAoKIyMgcHNldWRvdGltZSBmcm9tIHJhdyBnZW5lIGV4cHJlc3Npb24KCmBgYHtyLGVjaG89RixpbmNsdWRlPUYsZXZhbD1GfQppZihmaWxlLmV4aXN0cygiLi9jZHNfcmF3LnJkcyIpKXsKICBjZHM9cmVhZFJEUygiLi9jZHNfcmF3LnJkcyIpCn1lbHNlewogIGNlbGwubWV0YS5kYXRhPW9iajBAbWV0YS5kYXRhCiAgY2VsbC5tZXRhLmRhdGEkZGF0YXNldF9iYXRjaD1wbHlyOjptYXB2YWx1ZXMoY2VsbC5tZXRhLmRhdGEkYmF0Y2hfbGFiZWwsbmFtZXMobWFwcnVsZXMpLG1hcHJ1bGVzKQogIGdlbmVfYW5uPWRhdGEuZnJhbWUoZ2VuZV9zaG9ydF9uYW1lID0gbWFrZS51bmlxdWUocm93bmFtZXMocmF3LmRhdGEpKSxyb3cubmFtZXMgPSBtYWtlLnVuaXF1ZShyb3duYW1lcyhyYXcuZGF0YSkpKQogICNwZCA8LSBuZXcoIkFubm90YXRlZERhdGFGcmFtZSIsZGF0YT1jZWxsLm1ldGEuZGF0YSkKICAjZmQgPC0gbmV3KCJBbm5vdGF0ZWREYXRhRnJhbWUiLGRhdGE9Z2VuZV9hbm4pCiAgY2RzIDwtIG5ld19jZWxsX2RhdGFfc2V0KHJhdy5kYXRhLCBjZWxsX21ldGFkYXRhID0gY2VsbC5tZXRhLmRhdGEsZ2VuZV9tZXRhZGF0YSA9Z2VuZV9hbm4pCiAgIyMgU3RlcCAxOiBOb3JtYWxpemUgYW5kIHByZS1wcm9jZXNzIHRoZSBkYXRhCiAgY2RzIDwtIHByZXByb2Nlc3NfY2RzKGNkcywgbnVtX2RpbSA9IDMyLG1ldGhvZD0iUENBIixub3JtX21ldGhvZD0ibG9nIix2ZXJib3NlID0gRikKICAjIyBTdGVwIDI6IFJlbW92ZSBiYXRjaCBlZmZlY3RzIHdpdGggY2VsbCBhbGlnbm1lbnQKICAjI2NkcyA8LSBhbGlnbl9jZHMoY2RzLCBhbGlnbm1lbnRfZ3JvdXAgPSAiQmF0Y2hJRCIsIHJlc2lkdWFsX21vZGVsX2Zvcm11bGFfc3RyID0gTlVMTCkKICAjIyBTdGVwIDM6IFJlZHVjZSB0aGUgZGltZW5zaW9ucyB1c2luZyBVTUFQCiAgY2RzIDwtIHJlZHVjZV9kaW1lbnNpb24oY2RzLHJlZHVjdGlvbl9tZXRob2QgPSAiVU1BUCIscHJlcHJvY2Vzc19tZXRob2Q9IlBDQSIsdmVyYm9zZSA9IEYpCiAgCiAgIyMgU3RlcCA0OiBDbHVzdGVyIHRoZSBjZWxscwogIGNkcyA8LSBjbHVzdGVyX2NlbGxzKGNkcyxyZWR1Y3Rpb25fbWV0aG9kID0iVU1BUCIsY2x1c3Rlcl9tZXRob2QgPSAibGVpZGVuIix2ZXJib3NlID0gRikKICAjIENvbnN0cnVjdCB0aGUgZ3JhcGgKICAjIE5vdGUgdGhhdCwgZm9yIHRoZSByZXN0IG9mIHRoZSBjb2RlIHRvIHJ1biwgdGhlIGdyYXBoIHNob3VsZCBiZSBmdWxseSAocGFydGlvbmx5KSBjb25uZWN0ZWQKICAjIyBTdGVwIDU6IExlYXJuIGEgZ3JhcGgKICBjZHMgPC0gbGVhcm5fZ3JhcGgoY2RzLCB1c2VfcGFydGl0aW9uID0gVCx2ZXJib3NlID0gRikKICBjb2xEYXRhKGNkcykkY2x1c3RlcnM9Y2RzQGNsdXN0ZXJzJFVNQVAkY2x1c3RlcnMKfQpwcDE9cGxvdF9jZWxscyhjZHMsY29sb3JfY2VsbHNfYnkgPSAicGFydGl0aW9uIixsYWJlbF9jZWxsX2dyb3VwcyA9IEYpK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpwcDI9cGxvdF9jZWxscyhjZHMsY29sb3JfY2VsbHNfYnkgPSAiY2x1c3RlcnMiLGxhYmVsX2NlbGxfZ3JvdXBzPUYsZ3JhcGhfbGFiZWxfc2l6ZT0yLCBsYWJlbF9sZWF2ZXM9RixsYWJlbF9icmFuY2hfcG9pbnRzPUYpK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQpwPWNvd3Bsb3Q6OnBsb3RfZ3JpZChwcDEscHAyLGFsaWduID0gImgiLG5jb2wgPSAzKQoKIyMgU3RlcCA2OiBPcmRlciBjZWxscwojIGEgaGVscGVyIGZ1bmN0aW9uIHRvIGlkZW50aWZ5IHRoZSByb290IHByaW5jaXBhbCBwb2ludHM6CmdldF9lYXJsaWVzdF9wcmluY2lwYWxfbm9kZSA8LSBmdW5jdGlvbihjZHMsIGNsdXN0ZXI9YygiMSIsIjUiKSl7CiAgcm9vdF9wcl9ub2Rlcz1zYXBwbHkoY2x1c3RlcixmdW5jdGlvbihpaSl7CiAgICBjZWxsX2lkcyA8LSB3aGljaChjb2xEYXRhKGNkcylbLCAiY2x1c3RlcnMiXSAlaW4laWkpCiAgCiAgY2xvc2VzdF92ZXJ0ZXggPC1jZHNAcHJpbmNpcGFsX2dyYXBoX2F1eFtbIlVNQVAiXV0kcHJfZ3JhcGhfY2VsbF9wcm9qX2Nsb3Nlc3RfdmVydGV4CiAgCiAgY2xvc2VzdF92ZXJ0ZXggPC0gYXMubWF0cml4KGNsb3Nlc3RfdmVydGV4W2NvbG5hbWVzKGNkcyksIF0pCiAgcm9vdF9wcl9ub2RlcyA8LWlncmFwaDo6VihwcmluY2lwYWxfZ3JhcGgoY2RzKVtbIlVNQVAiXV0pJG5hbWVbYXMubnVtZXJpYyhuYW1lcyh3aGljaC5tYXgodGFibGUoY2xvc2VzdF92ZXJ0ZXhbY2VsbF9pZHMsXSkpKSldCiAgfSkKICByb290X3ByX25vZGVzCn0KIyByb290IGNlbGxzCmlkcz1nZXRfZWFybGllc3RfcHJpbmNpcGFsX25vZGUoY2RzLGNsdXN0ZXI9YygiMSIsIjQiLCI1IikpCmNkcyA8LSBvcmRlcl9jZWxscyhjZHMscm9vdF9wcl9ub2RlcyA9IGlkcykKI3Bsb3RfY2VsbHMoY2RzLGNvbG9yX2NlbGxzX2J5ID0gInBzZXVkb3RpbWUiKQoKI3NldCBwc2V1ZG90aW1lCmNvbERhdGEoY2RzKSRwc2V1ZG90aW1lPXBzZXVkb3RpbWUoY2RzKQpjb2xEYXRhKGNkcykkUHNldWRvdGltZT1jb2xEYXRhKGNkcykkcHNldWRvdGltZS9tYXgoY29sRGF0YShjZHMpJHBzZXVkb3RpbWUsbmEucm0gPSBUKQpzYXZlUkRTKGNkcyxmaWxlPSJjZHNfcmF3LnJkcyIpCmBgYAoKYGBge3J9CmNkcz1yZWFkUkRTKCIuL2Nkc19yYXcucmRzIikKYGBgCgpgYGB7cn0KcHJpbnQoc3VtKGNvbG5hbWVzKG9iajApPT1jb2xuYW1lcyhjZHMpKSkKIzEwODc4Cm9iajAkUHNldWRvdGltZV9yYXc9Y29sRGF0YShjZHMpJFBzZXVkb3RpbWUKCm9iajA9Tm9ybWFsaXplRGF0YShvYmowLHZlcmJvc2UgPSBGKQpvYmowPUZpbmRWYXJpYWJsZUZlYXR1cmVzKG9iajAsdmVyYm9zZSA9IEYpCmh2Zi5pbmZvPW9iajBAYXNzYXlzJFJOQUBtZXRhLmZlYXR1cmVzCmdlbmUyMj1odmYuaW5mb1tvcmRlcihodmYuaW5mbyR2c3QudmFyaWFuY2Uuc3RhbmRhcmRpemVkLCAgZGVjcmVhc2luZyA9IFRSVUUpLCAsZHJvcCA9IEZBTFNFXQpudW1fY2VsbHNfZXhwcmVzc2VkPU1hdHJpeDo6cm93U3VtcyhvYmowQGFzc2F5cyRSTkFAY291bnRzW3RmX2RmJEdlbmUsXSE9MCkKb2JqMD1TY2FsZURhdGEob2JqMCxmZWF0dXJlcyA9IHRmX2RmJEdlbmUsdmVyYm9zZSA9IEYpCmBgYAoKCmBgYHtyfQojc2NhbGVkCiNtID0gdChzY2FsZShtMSxjZW50ZXIgPSBUKSkKbT10KEZldGNoRGF0YShvYmowLHZhcnMgPSB0Zl9kZiRHZW5lLHNsb3QgPSAic2NhbGUuZGF0YSIpKQptW2lzLm5hbihtKV0gPSAwCm1bbSA+PSAzXSA9IDMKbVttIDw9IC0zXSA9IC0zCmhlYXRtYXBfbWF0cml4IDwtIG0Kcm93X2Rpc3QgPC0gYXMuZGlzdCgoMSAtIGNvcih0KGhlYXRtYXBfbWF0cml4KSkpLzIpCnJlczIgPC0gbGlzdChwaD1waGVhdG1hcDo6cGhlYXRtYXAoaGVhdG1hcF9tYXRyaXgsIHVzZVJhc3RlciA9IFQsIGNsdXN0ZXJfY29scyA9IEZBTFNFLCAKICAgICAgICBjbHVzdGVyX3Jvd3MgPSBULCBzaG93X3Jvd25hbWVzID0gRiwgc2hvd19jb2xuYW1lcyA9IEYsIAogICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2Vfcm93cyA9IHJvd19kaXN0LCBjbHVzdGVyaW5nX21ldGhvZCA9ICAid2FyZC5EMiIsIAogICAgICAgIGN1dHJlZV9yb3dzID0gMywgc2lsZW50ID0gVFJVRSwgZmlsZW5hbWUgPSBOQSksbT1oZWF0bWFwX21hdHJpeCkKIApgYGAKCgpgYGB7cn0Kej1yZXMyW1syXV1bcmVzMltbMV1dJHRyZWVfcm93JG9yZGVyLF0KQ2x1c3Rlcl9sYWJlbHM9Y3V0cmVlKHJlczJbWzFdXSR0cmVlX3JvdywzKVtyZXMyW1sxXV0kdHJlZV9yb3ckb3JkZXJdCnJvd19hbm5vPWRhdGEuZnJhbWUoZ2VuZW5hbWU9cm93bmFtZXMoeiksCiAgICAgICAgICAgICAgICAgICAgQ2x1c3Rlcj1DbHVzdGVyX2xhYmVscywKICAgICAgICAgICAgICAgICAgICBDbGFzcz10Zl9kZltyb3duYW1lcyh6KSwiR2VuZSJdLAogICAgICAgICAgICAgICAgICAgIFZhcmlhbmNlVHlwZT10Zl9kZltyb3duYW1lcyh6KSwiVmFyaWFuY2VUeXBlIl0sCiAgICAgICAgICAgICAgICAgICAgR2VuZVR5cGU9dGZfZGZbcm93bmFtZXMoeiksIkdlbmVUeXBlIl0sCiAgICAgICAgICAgICAgICAgICAgdnN0Lm1lYW49Z2VuZTIyW3Jvd25hbWVzKHopLCJ2c3QubWVhbiJdLAogICAgICAgICAgICAgICAgICAgIHZzdC52YXJpYW5jZS5zdGFuZGFyZD1nZW5lMjJbcm93bmFtZXMoeiksInZzdC52YXJpYW5jZS5zdGFuZGFyZGl6ZWQiXSwKICAgICAgICAgICAgICAgICAgICBudW1fY2VsbHNfZXhwcmVzc2VkPW51bV9jZWxsc19leHByZXNzZWRbcm93bmFtZXMoeildLAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IHJvd25hbWVzKHopLHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQpyb3dfYW5ubyRHZW5lQ2xhc3M9cGx5cjo6bWFwdmFsdWVzKHJvd19hbm5vJENsdXN0ZXIsZnJvbT1zb3J0KHVuaXF1ZShyb3dfYW5ubyRDbHVzdGVyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG89cGFzdGUwKCJNb2R1bGUiLGFzLm51bWVyaWMoZmFjdG9yKHNvcnQodW5pcXVlKENsdXN0ZXJfbGFiZWxzKSkpKSkpCgojb3JkZXJfaWQwPW9yZGVyKHJvd19hbm5vJENsdXN0ZXIscm93X2Fubm8kR2VuZVR5cGUscm93X2Fubm8kVmFyaWFuY2VUeXBlKQojej16W29yZGVyX2lkMCxdCiNyb3dfYW5ubz1yb3dfYW5ub1tvcmRlcl9pZDAsXQojR2VuZUNsYXNzLmNvbG9yPWdnX2NvbG9yX2h1ZShsZW5ndGgodW5pcXVlKENsdXN0ZXJfbGFiZWxzKSkpCgpHZW5lQ2xhc3MuY29sb3I9UkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDMsIkRhcmsyIikKbmFtZXMoR2VuZUNsYXNzLmNvbG9yKT1wYXN0ZTAoIk1vZHVsZSIsYXMubnVtZXJpYyhmYWN0b3Ioc29ydCh1bmlxdWUoQ2x1c3Rlcl9sYWJlbHMpKSkpKQojIGdlbmUgTW9kdWxlIGNvbG9yIApyb3dfYW5ubyRjb2xvcj1wbHlyOjptYXB2YWx1ZXMocm93X2Fubm8kR2VuZUNsYXNzLGZyb209bmFtZXMoR2VuZUNsYXNzLmNvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0bz1HZW5lQ2xhc3MuY29sb3IpCnJvd19hbm5vJEhWR19jb2xvcj1wbHlyOjptYXB2YWx1ZXMoYXMuY2hhcmFjdGVyKHJvd19hbm5vJFZhcmlhbmNlVHlwZSksZnJvbT1jKCJIVkciLCJMVkciKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0bz1jKCIjRTQxQTFDIiwiIzM3N0VCOCIpKQpgYGAKCmBgYHtyfQpyb3duYW1lcyhyb3dfYW5ubyk9cm93X2Fubm8kZ2VuZW5hbWUKcHNldWRvdGltZV9jb2xfZnVuID1jaXJjbGl6ZTo6Y29sb3JSYW1wMihzZXEoMCwgMSxsZW5ndGg9MTUwKSwgY29sb3JSYW1wUGFsZXR0ZShtb25vY2xlOjo6Ymx1ZTJncmVlbjJyZWQoMTUwKSkoMTUwKSkKIyBjb2x1bWFuIGFubm90YXRpb24KI2NvbF9hbm5vPWRhdGEuZnJhbWUocHNldWRvdGltZT1zZXEoMCwxLGxlbmd0aD1uY29sKHopKSwKIyAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gYXMuY2hhcmFjdGVyKDE6bmNvbCh6KSkpCmNvbF9hbm5vPWRhdGEuZnJhbWUoQmF0Y2hJRD1wbHlyOjptYXB2YWx1ZXMob2JqMCRCYXRjaElELGMoIk1IMDAxIiwiUlAwMDIiLCJSUDAwOSIpLGMoIlQxIiwiVDIiLCJUMyIpKSwKICAgICAgICAgICAgICAgICAgICBQc2V1ZG90aW1lPW9iajAkUHNldWRvdGltZV9yYXcsCiAgICAgICAgICAgICAgICAgICAgI1BzZXVkb3RpbWU9b2JqMCRQc2V1ZG90aW1lLAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IGNvbG5hbWVzKG9iajApKQpjb2xfaWQ9b3JkZXIoY29sX2Fubm8kUHNldWRvdGltZSkKej16Wyxjb2xfaWRdCmNvbF9hbm5vPWNvbF9hbm5vW2NvbF9pZCxdCiMgCnNldC5zZWVkKDEwKQoKcm93X2Fubm8udG1wPXJvd19hbm5vJT4lCiAgZmlsdGVyKHZzdC5tZWFuPjAuMSklPiUKICBncm91cF9ieShWYXJpYW5jZVR5cGUpJT4lCiAgYXJyYW5nZShkZXNjKHZzdC5tZWFuKSwuYnlfZ3JvdXAgPSBUUlVFKSU+JQogIG11dGF0ZShuX29yZGVyPTE6bigpKSU+JWFzLmRhdGEuZnJhbWUoKQoKI2lkLnNlbGVjdD1vcmRlcihyb3dfYW5ubyRxdmFsLGRlY3JlYXNpbmcgPSBGKVsxOjMwXQppZC5zZWxlY3Q9d2hpY2gocm93X2Fubm8kZ2VuZW5hbWUlaW4lcm93X2Fubm8udG1wJGdlbmVuYW1lW3Jvd19hbm5vLnRtcCRuX29yZGVyJWluJWMoMToyMCldKQpyb3dfYW5uby5uZXc9cm93X2Fubm9baWQuc2VsZWN0LF0KY29sX2Z1biA9IGNpcmNsaXplOjpjb2xvclJhbXAyKHNlcSgtMywgMyxsZW5ndGg9MjAwKSwgY29sb3JSYW1wUGFsZXR0ZShjKCJibHVlIiwgIndoaXRlIiwgInJlZCIpKSgyMDApKQojcHNldWRvdGltZV9jb2xfZnVuID1jaXJjbGl6ZTo6Y29sb3JSYW1wMihzZXEoMCwgMSxsZW5ndGg9MTUwKSwgY29sb3JSYW1wUGFsZXR0ZShjKCJkYXJrYmx1ZSIsImJsYWNrIiwiI0ZGRkYwMCIpKSgxNTApKQpwc2V1ZG90aW1lX2NvbF9mdW4gPWNpcmNsaXplOjpjb2xvclJhbXAyKHNlcSgwLCAxLGxlbmd0aD0xNTApLCBjb2xvclJhbXBQYWxldHRlKG1vbm9jbGU6OjpibHVlMmdyZWVuMnJlZCgxNTApKSgxNTApKQp0b3BfYW5ubz1IZWF0bWFwQW5ub3RhdGlvbiggUHNldWRvdGltZSA9IGFubm9fc2ltcGxlKGNvbF9hbm5vJFBzZXVkb3RpbWUsIGNvbCA9IHBzZXVkb3RpbWVfY29sX2Z1biksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBCYXRjaElEPWNvbF9hbm5vJEJhdGNoSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sPWxpc3QoQmF0Y2hJRD1jKCJUMSI9IiM2NkMyQTUiLCJUMiI9IiNGQzhENjIiLCJUMyI9IiM4REEwQ0IiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19sZWdlbmQgPSBjKFQsVCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9uYW1lX3NpZGUgPSAicmlnaHQiKQoKbGVmdF9hbm5vPXJvd0Fubm90YXRpb24oI0dlbmVDbGFzcz1yb3dfYW5ubyRHZW5lQ2xhc3MsCiAgICAgICAgICAgICAgICAgICAgICAgICNHZW5lVHlwZT1yb3dfYW5ubyRHZW5lVHlwZSwKICAgICAgICAgICAgICAgICAgICAgICAgIFZhcmlhbmNlVHlwZT1yb3dfYW5ubyRWYXJpYW5jZVR5cGUsCiAgICAgICAgICAgICAgICAgICAgICAjTm9uemVybz1hbm5vX2JhcnBsb3QobG9nMTAocm93X2Fubm8kbnVtX2NlbGxzX2V4cHJlc3NlZCksCiAgICAgICAgICAgICAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3AgID0gZ3BhcihmaWxsID0iIzAwQzRGRiIsY29sPU5BKSwKICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYXJfd2lkdGggPSAxLAogICAgICAgICAgICAgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodCA9IHVuaXQoMi41LCAiY20iKSksCiAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgYW5ub3RhdGlvbl9uYW1lX3JvdD05MCwKICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fbmFtZV9zaWRlID0gInRvcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICNjb2w9bGlzdChHZW5lQ2xhc3M9Y2VsbHR5cGVfY29sb3JbbmFtZXMoY2VsbHR5cGVfY29sb3IpJWluJXVuaXF1ZSh0bXAuYW5ubyRjZWxsdHlwZSldKSwKICAgICAgICAgICAgICAgICAgICAgICAgY29sPWxpc3QoR2VuZUNsYXNzPUdlbmVDbGFzcy5jb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVmFyaWFuY2VUeXBlPWMoIkhWRyI9IiNFNDFBMUMiLCJMVkciPSIjMzc3RUI4IikpLAogICAgICAgICAgICAgICAgICAgICAgICBzaG93X2Fubm90YXRpb25fbmFtZT1jKEYsVCksCiAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfbGVnZW5kPWMoVCxUKSkKcDI9Q29tcGxleEhlYXRtYXA6OkhlYXRtYXAoeiwgbmFtZSA9ICJzY2FsZWQuZXhwcmVzc2lvbiIsIAogICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9yb3dzID0gRiwKICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IEYsCiAgICAgICAgICAgICAgICAgICAgICBjb2w9Y29sX2Z1biwKICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9sYWJlbHMgPSAgcmVwKCIiLGxlbmd0aD1uY29sKHopKSwKICAgICAgICAgICAgICAgICAgICAgIHJvd19sYWJlbHMgPSByZXAoIiIsbGVuZ3RoPW5yb3coeikpLAogICAgICAgICAgICAgICAgICAgICAgI3Jvd19sYWJlbHMgPSBnZW5lLnVzZS5kZiRnZW5lLAogICAgICAgICAgICAgICAgICAgICAgcm93X25hbWVzX3NpZGUgPSAicmlnaHQiLAogICAgICAgICAgICAgICAgICAgICAgI3RoZSBmb2xsb3dpbmcgZm9udHNpemUgaXMgbm9udXNlZnVsbCBmb3IgdGhpcyBzaXR1YXR0aW9uCiAgICAgICAgICAgICAgICAgICAgICByb3dfbmFtZXNfZ3AgPSBncGFyKGZvbnRzaXplPTUpLAogICAgICAgICAgICAgICAgICAgICAgY29sdW1uX25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDgpLAogICAgICAgICAgICAgICAgICAgICAgI2NvbHVtbl9nYXAgPSB1bml0KDAuNSwgIm1tIiksCiAgICAgICAgICAgICAgICAgICAgICAjY29sdW1uX3NwbGl0ID0gdG1wLmFubm8kcmVzLAogICAgICAgICAgICAgICAgICAgICAgI2NvbHVtbl90aXRsZSA9ICIlcyIsCiAgICAgICAgICAgICAgICAgICAgICAjY29sdW1uX3RpdGxlX3JvdD05MCwKICAgICAgICAgICAgICAgICAgICAgICNyb3dfc3BsaXQgPWdlbmUudXNlLmRmJGNsdXN0ZXIsCiAgICAgICAgICAgICAgICAgICAgICByb3dfZ2FwID0gdW5pdCgwLjUsICJtbSIpLAogICAgICAgICAgICAgICAgICAgICAgI3Jvd19zcGxpdCA9IHJvd19hbm5vJEdlbmVDbGFzcywKICAgICAgICAgICAgICAgICAgICAgICNyb3dfdGl0bGUgPSAiJXMiLAogICAgICAgICAgICAgICAgICAgICAgcm93X3RpdGxlX2dwID0gZ3Bhcihmb250c2l6ZSA9IDEyKSwKICAgICAgICAgICAgICAgICAgICAgIHJvd190aXRsZV9yb3QgPSA5MCwKICAgICAgICAgICAgICAgICAgICAgIHRvcF9hbm5vdGF0aW9uID0gdG9wX2Fubm8sCiAgICAgICAgICAgICAgICAgICAgICBsZWZ0X2Fubm90YXRpb249IGxlZnRfYW5ubywKICAgICAgICAgICAgICAgICAgICAgICBoZWF0bWFwX2xlZ2VuZF9wYXJhbT1saXN0KGxlZ2VuZF9kaXJlY3Rpb249Imhvcml6b250YWwiLGxlZ2VuZF93aWR0aCA9IHVuaXQoMi41LCAiY20iKSksCiAgICAgICAgICAgICAgICAgICAgICByaWdodF9hbm5vdGF0aW9uID0gcm93QW5ub3RhdGlvbihmb289YW5ub19tYXJrKGF0PWlkLnNlbGVjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lkZT0icmlnaHQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHRlbmQ9dW5pdCgwLjIsImNtIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVsc19ncCA9IGdwYXIoZm9udHNpemU9OCxjb2w9cm93X2Fubm8kSFZHX2NvbG9yW2lkLnNlbGVjdF0pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9cm93X2Fubm8ubmV3JGdlbmVuYW1lKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFubm90YXRpb25fbmFtZV9zaWRlPSJ0b3AiKSkKYGBgCgoKYGBge3IsZmlnLndpZHRoPTksZmlnLmhlaWdodD04fQojZHJhdyhwMSxwYWRkaW5nPXVuaXQoYygyLDIsMiwyKSwibW0iKSkKbGdkX3BzZT1MZWdlbmQodGl0bGUgPSAiUHNldWRvdGltZSIsIGNvbCA9IHBzZXVkb3RpbWVfY29sX2Z1biwgYXQgPSBjKDAsMC41LDEpLCAKICAgIGxhYmVscyA9IGMoImxvdyIsIm1lZCIsImhpZ2giKSxsZWdlbmRfaGVpZ2h0ID0gdW5pdCgyLjUsICJjbSIpLGJvcmRlciA9IE5BLHRpdGxlX3Bvc2l0aW9uID0gInRvcGNlbnRlciIsZGlyZWN0aW9uPSJob3Jpem9udGFsIikKZHJhdyhwMixhbm5vdGF0aW9uX2xlZ2VuZF9saXN0ID0gbGlzdChsZ2RfcHNlKSxtZXJnZV9sZWdlbmRzPVQsaGVhdG1hcF9sZWdlbmRfc2lkZT0iYm90dG9tIikKYGBgCgpgYGB7cn0KI3NhdmUgcGxvdHMKc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkoY293cGxvdCkpCmxnZF9wc2U9TGVnZW5kKHRpdGxlID0gIlBzZXVkb3RpbWUiLCBjb2wgPSBwc2V1ZG90aW1lX2NvbF9mdW4sIGF0ID0gYygwLDAuNSwxKSwgCiAgICBsYWJlbHMgPSBjKCJsb3ciLCJtZWQiLCJoaWdoIiksbGVnZW5kX2hlaWdodCA9IHVuaXQoMi41LCAiY20iKSxib3JkZXIgPSBOQSx0aXRsZV9wb3NpdGlvbiA9ICJ0b3BjZW50ZXIiLGRpcmVjdGlvbj0iaG9yaXpvbnRhbCIpCgpjYWlyb19wZGYoImRlbm9pc2VkX2hlYXRtYXBfbm9zbW9vdGgucGRmIix3aWR0aD05LGhlaWdodD04KQogICAgZHJhdyhwMSxhbm5vdGF0aW9uX2xlZ2VuZF9saXN0ID0gbGlzdChsZ2RfcHNlKSxtZXJnZV9sZWdlbmRzPVQsaGVhdG1hcF9sZWdlbmRfc2lkZT0iYm90dG9tIikKZGV2Lm9mZigpCgp0aWZmKCJkZW5vaXNlZF9oZWF0bWFwX25vc21vb3RoLnRpZmYiLHVuaXRzPSJpbiIsY29tcHJlc3Npb249Imx6dyIscmVzPTMwMCx3aWR0aD05LGhlaWdodD04KQogICAgZHJhdyhwMSxhbm5vdGF0aW9uX2xlZ2VuZF9saXN0ID0gbGlzdChsZ2RfcHNlKSxtZXJnZV9sZWdlbmRzPVQsaGVhdG1hcF9sZWdlbmRfc2lkZT0iYm90dG9tIikKZGV2Lm9mZigpCgpjYWlyb19wZGYoInJhd19wc2V1ZG90aW1lX2hlYXRtYXBfbm9zbW9vdGgucGRmIix3aWR0aD05LGhlaWdodD04KQogICAgZHJhdyhwMixhbm5vdGF0aW9uX2xlZ2VuZF9saXN0ID0gbGlzdChsZ2RfcHNlKSxtZXJnZV9sZWdlbmRzPVQsaGVhdG1hcF9sZWdlbmRfc2lkZT0iYm90dG9tIikKZGV2Lm9mZigpCgp0aWZmKCJyYXdfcHNldWRvdGltZV9oZWF0bWFwX25vc21vb3RoLnRpZmYiLHVuaXRzPSJpbiIsY29tcHJlc3Npb249Imx6dyIscmVzPTMwMCx3aWR0aD05LGhlaWdodD04KQogICAgZHJhdyhwMixhbm5vdGF0aW9uX2xlZ2VuZF9saXN0ID0gbGlzdChsZ2RfcHNlKSxtZXJnZV9sZWdlbmRzPVQsaGVhdG1hcF9sZWdlbmRfc2lkZT0iYm90dG9tIikKZGV2Lm9mZigpCmBgYAoKIyByZWFkaW5nIHJlc3VsdHMgZnJvbSBvdGhlciBtZXRob2RzIAoKYGBge3J9CmFkPWltcG9ydCgiYW5uZGF0YSIsY29udmVydCA9IEZBTFNFKQphZGF0YT1hZCRyZWFkX2g1YWQoIi4uLy4uL2RjYV90ZXN0Lmg1YWQiKQpvYmpfcmF3PUNvbnZlcnRfdG9fc2V1cmF0MyhhZGF0YSkKb2JqX3Jhdz1Ob3JtYWxpemVEYXRhKG9ial9yYXcsdmVyYm9zZSA9IEYpCm9ial9yYXc9RmluZFZhcmlhYmxlRmVhdHVyZXMob2JqX3Jhdyx2ZXJib3NlID0gRikKeHhfdnN0Lm1lYW49b2JqX3Jhd0Bhc3NheXMkUk5BQG1ldGEuZmVhdHVyZXNbLGMoInZzdC5tZWFuIiksZHJvcD1GXQp4eF92c3QubWVhbiRnZW5lPXJvd25hbWVzKHh4X3ZzdC5tZWFuKQoKcmF3LmRhdGE9b2JqX3Jhd0Bhc3NheXMkUk5BQGNvdW50cwpgYGAKCgpgYGB7cn0KbWFwcnVsZXM9YygiMjAxN18wODAxIj0iVDEiLCIyMDE3XzEwMTciPSJUMiIsIjIwMTdfMTEyMCI9IlQzIikKbWFwcnVsZXMKCk1ldGhvZHNfY29sb3I9YygiI0U0MUExQyIsIiMzNzdFQjgiLCIjNERBRjRBIiwiIzk4NEVBMyIsIiNGRjdGMDAiLCIjMDBjYzk5IikKbmFtZXMoTWV0aG9kc19jb2xvcik9Yygic2NWSSIsIkNhckRFQyIsIkRDQSIsIk1OTiIsIlJhdyIsIlNjYW5vcmFtYSIpCmBgYAoKYGBge3IsZmlnLndpZHRoPTcsZmlnLmhlaWdodD0zfQpvcD1wYXIobWFyPWMoNSw0LDYsNCkpCmltYWdlKDE6bGVuZ3RoKE1ldGhvZHNfY29sb3IpLDEsIGFzLm1hdHJpeCgxOmxlbmd0aChNZXRob2RzX2NvbG9yKSksY29sPU1ldGhvZHNfY29sb3IseGxhYiA9ICIiLCB5bGFiID0gIiIpCmF4aXMoMyxhdD1zZXEoMTpsZW5ndGgoTWV0aG9kc19jb2xvcikpLGxhYmVscz1NZXRob2RzX2NvbG9yLGxhcz0yLGx3ZD0wKQpwYXIob3ApCmBgYAoKCmBgYHtyfQphZGF0YT1hZCRyZWFkX2g1YWQoIi4uL0NhckRFQyBSZXN1bHRzL2FkYXRhX0NhckRFQy5oNWFkIikKYGBgCgoKYGBge3J9CmNlbGwubWV0YS5kYXRhPXB5X3RvX3IoYWRhdGEkb2JzKQpjZWxsLm1ldGEuZGF0YSRkYXRhc2V0X2JhdGNoPXBseXI6Om1hcHZhbHVlcyhjZWxsLm1ldGEuZGF0YSRiYXRjaF9sYWJlbCxuYW1lcyhtYXBydWxlcyksbWFwcnVsZXMpCmdlbmVfYW5uX2NhcmRlYz1weV90b19yKGFkYXRhJHZhcikKCm10eD10KHB5X3RvX3IoYWRhdGEkbGF5ZXJzWydkZW5vaXNlZCBjb3VudHMnXSkpCmNvbG5hbWVzKG10eCk9Y2VsbC5tZXRhLmRhdGEkY2VsbG5hbWUKcm93bmFtZXMobXR4KT1yb3duYW1lcyhnZW5lX2Fubl9jYXJkZWMpCm10eF9zaXplZmFjdG9yPTFlNC9jb2xTdW1zKG10eCkKYGBgCgpgYGB7cn0Kb2JqX2NhcmRlYz1DcmVhdGVTZXVyYXRPYmplY3QobXR4LG1ldGEuZGF0YSA9IGNlbGwubWV0YS5kYXRhKQpvYmpfY2FyZGVjPU5vcm1hbGl6ZURhdGEob2JqX2NhcmRlYyx2ZXJib3NlID0gRikKSWRlbnRzKG9ial9jYXJkZWMpPSJCYXRjaElEIiAjb2JqMSBtZWFucyBkZW5vaXNlZCBjb3VudCBieSBDYXJERUMKYGBgCgoKYGBge3J9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KG1vbm9jbGUzKSkKY2RzPXJlYWRSRFMoIi4vY2RzX2NhcmRlYy5yZHMiKQpvYmpfY2FyZGVjJFBzZXVkb3RpbWU9Y29sRGF0YShjZHMpJFBzZXVkb3RpbWUKIy9tYXgoY29sRGF0YShjZHMpJFBzZXVkb3RpbWUsbmEucm09VCkKb2JqX3JhdyRQc2V1ZG90aW1lPWNvbERhdGEoY2RzKSRQc2V1ZG90aW1lCiMvbWF4KGNvbERhdGEoY2RzKSRQc2V1ZG90aW1lLG5hLnJtPVQpCnJtKGNkcykKZ2MoKQpjZHM9cmVhZFJEUygiLi9jZHNfcmF3LnJkcyIpCm9ial9yYXckUHNldWRvdGltZV9yYXc9Y29sRGF0YShjZHMpJFBzZXVkb3RpbWUKIy9tYXgoY29sRGF0YShjZHMpJFBzZXVkb3RpbWUsbmEucm09VCkKYGBgCgojIyBEQ0ErY29tYmF0CgpgYGB7cn0KYWRhdGE9YWQkcmVhZF9oNWFkKCIuLi9maW5hbF9wcm9jZXNzZWRfcmVzdWx0cy9kY2EgUmVzdWx0cyBOZXcvYWRhdGFfYWxsLmg1YWQiKQpgYGAKCmBgYHtyfQpjZWxsLm1ldGEuZGF0YT1weV90b19yKGFkYXRhJG9icykKY2VsbC5tZXRhLmRhdGEkZGF0YXNldF9iYXRjaD1wbHlyOjptYXB2YWx1ZXMoY2VsbC5tZXRhLmRhdGEkYmF0Y2hfbGFiZWwsbmFtZXMobWFwcnVsZXMpLG1hcHJ1bGVzKQpnZW5lX2FubjA9cHlfdG9fcihhZGF0YSR2YXIpCmdlbmVfYW5uPWRhdGEuZnJhbWUoZ2VuZV9zaG9ydF9uYW1lID0gbWFrZS51bmlxdWUocm93bmFtZXMoZ2VuZV9hbm4wKSksCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gbWFrZS51bmlxdWUocm93bmFtZXMoZ2VuZV9hbm4wKSkpCm10eD10KHB5X3RvX3IoYWRhdGEkWCkpCmNvbG5hbWVzKG10eCk9Y2VsbC5tZXRhLmRhdGEkY2VsbG5hbWUKcm93bmFtZXMobXR4KT1yb3duYW1lcyhnZW5lX2FubikKbXR4X3NpemVmYWN0b3I9MWU0L2NvbFN1bXMobXR4KQpvYmpfZGNhPUNyZWF0ZVNldXJhdE9iamVjdChtdHgsbWV0YS5kYXRhPWNlbGwubWV0YS5kYXRhKQojb2JqX2RjYT1Ob3JtYWxpemVEYXRhKG9iamVjdCA9IG9ial9kY2EsdmVyYm9zZSA9IEYpCmNkcz1yZWFkUkRTKCIuL2Nkc19kY2EucmRzIikKb2JqX2RjYSRQc2V1ZG90aW1lPWNvbERhdGEoY2RzKSRQc2V1ZG90aW1lCiMvbWF4KGNvbERhdGEoY2RzKSRQc2V1ZG90aW1lLG5hLnJtPVQpCmBgYAoKIyMgc2NWSQoKYGBge3J9CmFkYXRhPWFkJHJlYWRfaDVhZCgiLi4vZmluYWxfcHJvY2Vzc2VkX3Jlc3VsdHMvc2NWSSBSZXN1bHRzIE5ldy9tb25vY3l0ZXNfQUxML2FkYXRhX2FsbC5oNWFkIikKYGBgCgpgYGB7cn0KY2VsbC5tZXRhLmRhdGE9cHlfdG9fcihhZGF0YSRvYnMpCmNlbGwubWV0YS5kYXRhJGRhdGFzZXRfYmF0Y2g9cGx5cjo6bWFwdmFsdWVzKGNlbGwubWV0YS5kYXRhJGJhdGNoX2xhYmVsLG5hbWVzKG1hcHJ1bGVzKSxtYXBydWxlcykKZ2VuZV9hbm4wPXB5X3RvX3IoYWRhdGEkdmFyKQpnZW5lX2Fubj1kYXRhLmZyYW1lKGdlbmVfc2hvcnRfbmFtZSA9IG1ha2UudW5pcXVlKHJvd25hbWVzKGdlbmVfYW5uMCkpLAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IG1ha2UudW5pcXVlKHJvd25hbWVzKGdlbmVfYW5uMCkpKQptdHg9dChweV90b19yKGFkYXRhJFgpKQpjb2xuYW1lcyhtdHgpPWNlbGwubWV0YS5kYXRhJGNlbGxuYW1lCnJvd25hbWVzKG10eCk9cm93bmFtZXMoZ2VuZV9hbm4pCm10eF9zaXplZmFjdG9yPTFlNC9jb2xTdW1zKG10eCkKb2JqX3Njdmk9Q3JlYXRlU2V1cmF0T2JqZWN0KG10eCxtZXRhLmRhdGE9Y2VsbC5tZXRhLmRhdGEpCiNvYmpfc2N2aT1Ob3JtYWxpemVEYXRhKG9iamVjdCA9IG9ial9zY3ZpLHZlcmJvc2UgPSBGKQpjZHM9cmVhZFJEUygiLi9jZHNfc2N2aS5yZHMiKQpvYmpfc2N2aSRQc2V1ZG90aW1lPWNvbERhdGEoY2RzKSRQc2V1ZG90aW1lCiMvbWF4KGNvbERhdGEoY2RzKSRQc2V1ZG90aW1lLG5hLnJtID0gVCkKYGBgCgojIyBNTk4KCmBgYHtyfQpvdXRwdXQ9cmVhZFJEUygiLi4vZmluYWxfcHJvY2Vzc2VkX3Jlc3VsdHMvTU5OX2NvcnJlY3RlZF9hbGwucmRzIikKbXR4PW91dHB1dEBhc3NheXMkZGF0YSRjb3JyZWN0ZWQKY2VsbC5tZXRhLmRhdGE9Y29sRGF0YShvdXRwdXQpCmNlbGwubWV0YS5kYXRhJGRhdGFzZXRfYmF0Y2g9cGx5cjo6bWFwdmFsdWVzKGNlbGwubWV0YS5kYXRhJGJhdGNoX2xhYmVsLG5hbWVzKG1hcHJ1bGVzKSxtYXBydWxlcykKZ2VuZV9hbm49ZGF0YS5mcmFtZShnZW5lX3Nob3J0X25hbWUgPSBtYWtlLnVuaXF1ZShyb3duYW1lcyhtdHgpKSwKICAgICAgICAgICAgICAgICAgICByb3cubmFtZXMgPSBtYWtlLnVuaXF1ZShyb3duYW1lcyhtdHgpKSkKb2JqX21ubj1DcmVhdGVTZXVyYXRPYmplY3QobXR4LG1ldGEuZGF0YT1hcy5kYXRhLmZyYW1lKGNlbGwubWV0YS5kYXRhKSkKY2RzPXJlYWRSRFMoIi4vY2RzX21ubi5yZHMiKQpvYmpfbW5uJFBzZXVkb3RpbWU9Y29sRGF0YShjZHMpJFBzZXVkb3RpbWUKIy9tYXgoY29sRGF0YShjZHMpJFBzZXVkb3RpbWUsbmEucm09VCkKYGBgCgojIyBzY2Fub3JhbWEKCmBgYHtyfQojYWRkIHdoZW4gcmV2aXNlZCBpbiBHZW5vbWUgUmVzZWFyY2gKYWRhdGE9YWQkcmVhZF9oNWFkKCIuLi9maW5hbF9wcm9jZXNzZWRfcmVzdWx0cy9zY2Fub3JhbWEgUmVzdWx0cy9hZGF0YV9BTEwuaDVhZCIpIwpjZWxsLm1ldGEuZGF0YT1weV90b19yKGFkYXRhJG9icykKY2VsbC5tZXRhLmRhdGEkZGF0YXNldF9iYXRjaD1wbHlyOjptYXB2YWx1ZXMoY2VsbC5tZXRhLmRhdGEkYmF0Y2hfbGFiZWwsbmFtZXMobWFwcnVsZXMpLG1hcHJ1bGVzKQpnZW5lX2FubjA9cHlfdG9fcihhZGF0YSRyYXckdmFyKQpnZW5lX2Fubj1kYXRhLmZyYW1lKGdlbmVfc2hvcnRfbmFtZSA9IG1ha2UudW5pcXVlKHJvd25hbWVzKGdlbmVfYW5uMCkpLAogICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IG1ha2UudW5pcXVlKHJvd25hbWVzKGdlbmVfYW5uMCkpKQptdHg9dChweV90b19yKGFkYXRhJHJhdyRYJHRvY3NjKCkpKSMKY29sbmFtZXMobXR4KT1jZWxsLm1ldGEuZGF0YSRjZWxsbmFtZQpyb3duYW1lcyhtdHgpPXJvd25hbWVzKGdlbmVfYW5uKQpvYmpfc2Nhbm9yYW1hPUNyZWF0ZVNldXJhdE9iamVjdChtdHgsbWV0YS5kYXRhPWFzLmRhdGEuZnJhbWUoY2VsbC5tZXRhLmRhdGEpKQojb2JqX3NjYW5vcmFtYT1Ob3JtYWxpemVEYXRhKG9ial9zY2Fub3JhbWEsdmVyYm9zZSA9IEYpCmNkcz1yZWFkUkRTKCIuL2Nkc19zY2Fub3JhbWEucmRzIikKb2JqX3NjYW5vcmFtYSRQc2V1ZG90aW1lPWNvbERhdGEoY2RzKSRQc2V1ZG90aW1lCiMvbWF4KGNvbERhdGEoY2RzKSRQc2V1ZG90aW1lLG5hLnJtPVQpCmBgYAoKYGBge3J9CmF2Z19leHA9bG9nMXAoU2V1cmF0Ojo6RmFzdEV4cE1lYW4ob2JqX2NhcmRlY0Bhc3NheXMkUk5BQGNvdW50cyxkaXNwbGF5X3Byb2dyZXNzID0gRikpCmdlbmVfYW5uX2FsbD1kYXRhLmZyYW1lKGdlbmVfc2hvcnRfbmFtZSA9IG1ha2UudW5pcXVlKHJvd25hbWVzKG9ial9jYXJkZWMpKSwKICAgICAgICAgICAgICAgICAgICBWYXJpYW5jZVR5cGU9Z2VuZV9hbm5fY2FyZGVjJGBWYXJpYW5jZSBUeXBlYCwKICAgICAgICAgICAgICAgICAgICBhdmdfZXhwPWF2Z19leHAsCiAgICAgICAgICAgICAgICAgICAgcm93Lm5hbWVzID0gbWFrZS51bmlxdWUocm93bmFtZXMob2JqX2NhcmRlYykpKQpgYGAKCmBgYHtyfQp0Zl9kZj1vcGVueGxzeDo6cmVhZC54bHN4KCIuL1RGX2Jsb29kLnhsc3giKSU+JWZpbHRlcihHZW5lJWluJXJvd25hbWVzKG9ial9yYXcpKSU+JQogIGxlZnRfam9pbihnZW5lX2Fubl9hbGwsYnk9YygiR2VuZSI9ImdlbmVfc2hvcnRfbmFtZSIpKSU+JQogIGxlZnRfam9pbih4eF92c3QubWVhbixieT1jKCJHZW5lIj0iZ2VuZSIpKSU+JQogIGFzLmRhdGEuZnJhbWUoKQpyb3duYW1lcyh0Zl9kZik9dGZfZGYkR2VuZQpEVDo6ZGF0YXRhYmxlKHRmX2RmKQpgYGAKCgpBZnRlciBmaWx0ZXJpbmcgb3V0IGdlbmUgZXhwcmVzc2lvbiA8MC4wNSwgd2UgaGF2ZSAKCmBgYHtyfQp0Zl9kZj1zdWJzZXQodGZfZGYsdGZfZGYkYXZnX2V4cD49MC4wNSApCkRUOjpkYXRhdGFibGUodGZfZGYpCmBgYAoKYGBge3J9CiN3cml0ZS50YWJsZSh0Zl9kZlssYygxLDIsMyldLGZpbGUgPSAidG1wLmNzdiIsc2VwPSIsIixjb2wubmFtZXMgPSBULHJvdy5uYW1lcyA9IEYpCmBgYAoKYGBge3J9CmdldF9yYXdfZGVub2lzZWRfZGZfbmV3PWZ1bmN0aW9uKG9iaixnZW5lKXsKICAjZGYwPWRhdGEuZnJhbWUoY2JpbmQocHNldWRvdGltZT1vYmowJFBzZXVkb3RpbWUsRmV0Y2hEYXRhKG9iajAsdmFycz1nZW5lKSkpCiAgZGYwPUZldGNoRGF0YShvYmplY3QgPSBvYmosdmFycz1nZW5lKQogIGRmMCRwc2V1ZG90aW1lPW9iaiRQc2V1ZG90aW1lCiAgZGYwJFBzZXVkb3RpbWVfMDE9b2JqJFBzZXVkb3RpbWUvbWF4KG9iaiRQc2V1ZG90aW1lLG5hLnJtID0gVCkjIGZvciBwbG90IAogICNjb2xuYW1lcyhkZjApWzI6bmNvbChkZjApXT1nc3ViKHBhdHRlcm4gPSAiLSIscmVwbGFjZW1lbnQgPSAiIix4ID1nZW5lICkKICBkZjAkQmF0Y2hJRD1wbHlyOjptYXB2YWx1ZXMob2JqJGJhdGNoX2xhYmVsLG5hbWVzKG1hcHJ1bGVzKSxtYXBydWxlcykKICBkZjA9ZGYwW2lzLmZpbml0ZShkZjAkcHNldWRvdGltZSksXQogIGRmMD1kZjBbb3JkZXIoZGYwJHBzZXVkb3RpbWUsZGVjcmVhc2luZyA9IEYpLCxkcm9wPUZdCiAgZGYwJHg9ZGYwJHBzZXVkb3RpbWUvbWF4KGRmMCRwc2V1ZG90aW1lKQogIHJldHVybihkZjApCn0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKGxpYnJhcnkobWdjdikpCgpnZXRfcHZhbF9wYWlyX25ldz1mdW5jdGlvbihkZjAwLGdlbmUscmV0dXJuX3B2YWxfdGFibGU9VCl7CiAgZGYwMF90bXA9ZGYwMAogIGlkX2dlbmU9d2hpY2goY29sbmFtZXMoZGYwMF90bXApPT1nZW5lKQogIGdlbmVfbmV3PWdzdWIoIi18XFwuIiwiIixnZW5lKQogIGNvbG5hbWVzKGRmMDBfdG1wKVtpZF9nZW5lXT1nZW5lX25ldwogIAogIGZvcm11bGExPWFzLmZvcm11bGEocGFzdGUwKGdlbmVfbmV3LCJ+Iiwicyhwc2V1ZG90aW1lLGJzPVwiY3NcIikiKSkKICBmb3JtdWxhMj1hcy5mb3JtdWxhKHBhc3RlMChnZW5lX25ldywifkJhdGNoSUQrIiwicyhwc2V1ZG90aW1lLGJzPVwiY3NcIikiKSkKICBtb2QxIDwtIGdhbShmb3JtdWxhMSwgZGF0YSA9IGRmMDBfdG1wLCBzZWxlY3QgPSBGKQogIG1vZDIgPC0gZ2FtKGZvcm11bGEyLCBkYXRhID0gZGYwMF90bXAsIHNlbGVjdCA9IEYpCiAgbTE9YW5vdmEobW9kMSwgbW9kMiwgdGVzdCA9ICJDaGlzcSIpCiAgbTI9YW5vdmEobW9kMSwgbW9kMiwgdGVzdCA9ICJGIikKICB1bmlxdWVfYmF0Y2g9c29ydChhcy5jaGFyYWN0ZXIodW5pcXVlKGRmMDBfdG1wJEJhdGNoSUQpKSxkZWNyZWFzaW5nID0gVCkKICByZXNfcGFpcndpc2U9bGFwcGx5KHVuaXF1ZV9iYXRjaCxmdW5jdGlvbih4KXsKICAgIGRmMHRtcD1kZjAwX3RtcFtkZjAwX3RtcCRCYXRjaElEIT14LCxkcm9wPUZdCiAgICBtb2QxIDwtIGdhbShmb3JtdWxhMSwgZGF0YSA9IGRmMHRtcCwgc2VsZWN0ID0gRikKICAgIG1vZDIgPC0gZ2FtKGZvcm11bGEyLCBkYXRhID0gZGYwdG1wLCBzZWxlY3QgPSBGKQogICAgbTE9YW5vdmEobW9kMSwgbW9kMiwgdGVzdCA9ICJDaGlzcSIpCiAgICBtMj1hbm92YShtb2QxLCBtb2QyLCB0ZXN0ID0gIkYiKQogICAgcmV0dXJuKGxpc3QobW9kMT1tb2QxLG1vZDI9bW9kMixtMT1tMSxtMj1tMikpCiAgfSkKICBuYW1lcyhyZXNfcGFpcndpc2UpPXVuaXF1ZV9iYXRjaAogIAogIGlmKHJldHVybl9wdmFsX3RhYmxlKXsKICAgIGNvbF9uYW1lcz1jKCJnZW5lIixwYXN0ZTAocmVwKGMoIlQxIHYucy4gVDIiLCAiVDEgdi5zLiBUMyIsIlQyIHYucy4gVDMiKSx0aW1lcz0yKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVwKGMoIiAoQ2hpc3EpIiwiIChGKSIpLGVhY2g9MykpLAogICAgICAgICAgICAgICAgIk92ZXJhbGwgKENoaXNxKSIsICJPdmVyYWxsIChGKSIKICAgICAgICAgICAgICAgICkKICAgIHJlczA9ZGF0YS5mcmFtZShtYXRyaXgoTkEsbmNvbD1sZW5ndGgoY29sX25hbWVzKSxucm93PTEpKQogICAgY29sbmFtZXMocmVzMCk9Y29sX25hbWVzCiAgICByZXMwWzEsMV09Z2VuZQogICAgcmVzMFsxLDJdPXJlc19wYWlyd2lzZSRUMyRtMSRgUHIoPkNoaSlgWzJdCiAgICByZXMwWzEsM109cmVzX3BhaXJ3aXNlJFQyJG0xJGBQcig+Q2hpKWBbMl0KICAgIHJlczBbMSw0XT1yZXNfcGFpcndpc2UkVDEkbTEkYFByKD5DaGkpYFsyXQogICAgcmVzMFsxLDVdPXJlc19wYWlyd2lzZSRUMyRtMiRgUHIoPkYpYFsyXQogICAgcmVzMFsxLDZdPXJlc19wYWlyd2lzZSRUMiRtMiRgUHIoPkYpYFsyXQogICAgcmVzMFsxLDddPXJlc19wYWlyd2lzZSRUMSRtMiRgUHIoPkYpYFsyXQogICAgcmVzMFsxLDhdPW0xJGBQcig+Q2hpKWBbMl0KICAgIHJlczBbMSw5XT1tMiRgUHIoPkYpYFsyXQogICAgcmV0dXJuKHJlczApCiAgfWVsc2V7CiAgICByZXR1cm4obGlzdChzaW5nbGU9bGlzdChtb2QxPW1vZDEsbW9kMj1tb2QyLG0xPW0xLG0yPW0yKSwKICAgICAgICAgICAgICBwYXJpd2lzZT1yZXNfcGFpcndpc2UpKSAgCiAgfQogIAp9CmBgYAoKYGBge3IsZWNobz1GLGluY2x1ZGU9RixldmFsPUZ9CiMgZ2V0IHRoZSBwLXZhbHVlCm9ial9uYW1lX2xpc3Q9Yygib2JqX3JhdyIsIm9ial9zY2Fub3JhbWEiLCJvYmpfY2FyZGVjIiwib2JqX3NjdmkiLCJvYmpfZGNhIiwib2JqX21ubiIpCgpkZjBfbGlzdD1sYXBwbHkob2JqX25hbWVfbGlzdCxmdW5jdGlvbih4KSBnZXRfcmF3X2Rlbm9pc2VkX2RmX25ldyhnZXQoeCksZ2VuZT10Zl9kZiRHZW5lKSkKbmFtZXMoZGYwX2xpc3QpPW9ial9uYW1lX2xpc3QKCnJlc19wdmFsX3Jhdz1kby5jYWxsKCJyYmluZCIsbGFwcGx5KHRmX2RmJEdlbmUsZnVuY3Rpb24oeCl7CiAgICB0bXAwMD1nZXRfcHZhbF9wYWlyX25ldyhkZjBfbGlzdFtbIm9ial9yYXciXV0sZ2VuZT14KSNyYXcKICAgIHRtcDAwJGdlbmV0eXBlPWFzLmNoYXJhY3Rlcih0Zl9kZiRWYXJpYW5jZVR5cGVbdGZfZGYkR2VuZT09eF0pCiAgICByZXR1cm4odG1wMDApCiAgfSkpCnJlc19wdmFsX3JhdyRHcm91cD0iUmF3XzAiCgoKcmVzX3B2YWxfY2FyZGVjPWRvLmNhbGwoInJiaW5kIixsYXBwbHkodGZfZGYkR2VuZSxmdW5jdGlvbih4KXsKICAgdG1wMDA9Z2V0X3B2YWxfcGFpcl9uZXcoZGYwX2xpc3RbWyJvYmpfY2FyZGVjIl1dLGdlbmU9eCkgI2Rlbm9pc2VkCiAgIHRtcDAwJGdlbmV0eXBlPWFzLmNoYXJhY3Rlcih0Zl9kZiRWYXJpYW5jZVR5cGVbdGZfZGYkR2VuZT09eF0pCiAgcmV0dXJuKHRtcDAwKQogIH0pKQpyZXNfcHZhbF9jYXJkZWMkR3JvdXA9IkNhckRFQyIKCnJlc19wdmFsX3Njdmk9ZG8uY2FsbCgicmJpbmQiLGxhcHBseSh0Zl9kZiRHZW5lLGZ1bmN0aW9uKHgpewogICB0bXAwMD1nZXRfcHZhbF9wYWlyX25ldyhkZjBfbGlzdFtbIm9ial9zY3ZpIl1dLGdlbmU9eCkgI2Rlbm9pc2VkCiAgIHRtcDAwJGdlbmV0eXBlPXRmX2RmJFZhcmlhbmNlVHlwZVt0Zl9kZiRHZW5lPT14XQogIHJldHVybih0bXAwMCkKICB9KSkKcmVzX3B2YWxfc2N2aSRHcm91cD0ic2NWSSIKCnJlc19wdmFsX2RjYT1kby5jYWxsKCJyYmluZCIsbGFwcGx5KHRmX2RmJEdlbmUsZnVuY3Rpb24oeCl7CiAgIHRtcDAwPWdldF9wdmFsX3BhaXJfbmV3KGRmMF9saXN0W1sib2JqX2RjYSJdXSxnZW5lPXgpICNkZW5vaXNlZAogICB0bXAwMCRnZW5ldHlwZT1hcy5jaGFyYWN0ZXIodGZfZGYkVmFyaWFuY2VUeXBlW3RmX2RmJEdlbmU9PXhdKQogIHJldHVybih0bXAwMCkKICB9KSkKcmVzX3B2YWxfZGNhJEdyb3VwPSJEQ0EiCgpyZXNfcHZhbF9tbm49ZG8uY2FsbCgicmJpbmQiLGxhcHBseSh0Zl9kZiRHZW5lLGZ1bmN0aW9uKHgpewogICB0bXAwMD1nZXRfcHZhbF9wYWlyX25ldyhkZjBfbGlzdFtbIm9ial9tbm4iXV0sZ2VuZT14KSAjZGVub2lzZWQKICAgdG1wMDAkZ2VuZXR5cGU9YXMuY2hhcmFjdGVyKHRmX2RmJFZhcmlhbmNlVHlwZVt0Zl9kZiRHZW5lPT14XSkKICByZXR1cm4odG1wMDApCiAgfSkpCnJlc19wdmFsX21ubiRHcm91cD0iTU5OIgoKcmVzX3B2YWxfc2Nhbm9yYW1hPWRvLmNhbGwoInJiaW5kIixsYXBwbHkodGZfZGYkR2VuZSxmdW5jdGlvbih4KXsKICAgdG1wMDA9Z2V0X3B2YWxfcGFpcl9uZXcoZGYwX2xpc3RbWyJvYmpfc2Nhbm9yYW1hIl1dLGdlbmU9eCkgI2Rlbm9pc2VkCiAgIHRtcDAwJGdlbmV0eXBlPWFzLmNoYXJhY3Rlcih0Zl9kZiRWYXJpYW5jZVR5cGVbdGZfZGYkR2VuZT09eF0pCiAgcmV0dXJuKHRtcDAwKQogIH0pKQpyZXNfcHZhbF9zY2Fub3JhbWEkR3JvdXA9IlNjYW5vcmFtYSIKCgojcHN1ZWRvdGltZSBmcm9tIHJhdwpkZjA9RmV0Y2hEYXRhKG9ial9yYXcsdmFycz10Zl9kZiRHZW5lKQpkZjAkcHNldWRvdGltZT1vYmpfcmF3JFBzZXVkb3RpbWVfcmF3CmRmMCRCYXRjaElEPW9ial9jYXJkZWMkZGF0YXNldF9iYXRjaApkZjA9ZGYwW2lzLmZpbml0ZShkZjAkcHNldWRvdGltZSksXQpkZjA9ZGYwW29yZGVyKGRmMCRwc2V1ZG90aW1lLGRlY3JlYXNpbmcgPSBGKSwsZHJvcD1GXQpkZjAkeD1kZjAkcHNldWRvdGltZS9tYXgoZGYwJHBzZXVkb3RpbWUpCiAKcmVzX3B2YWxfcmF3XzI9ZG8uY2FsbCgicmJpbmQiLGxhcHBseSh0Zl9kZiRHZW5lLGZ1bmN0aW9uKHgpewogdG1wMDA9Z2V0X3B2YWxfcGFpcl9uZXcoZGYwLGdlbmU9eCkgI2Rlbm9pc2VkCiB0bXAwMCRnZW5ldHlwZT1hcy5jaGFyYWN0ZXIodGZfZGYkVmFyaWFuY2VUeXBlW3RmX2RmJEdlbmU9PXhdKQogIHJldHVybih0bXAwMCkKfSkpCnJlc19wdmFsX3Jhd18yJEdyb3VwPSJSYXciCgpgYGAKCmBgYHtyLGVjaG89RixpbmNsdWRlPUYsZXZhbD1GfQpzYXZlUkRTKGxpc3QocmVzX3B2YWxfcmF3PXJlc19wdmFsX3JhdywKICAgICAgICAgICAgIHJlc19wdmFsX3Jhd18yPXJlc19wdmFsX3Jhd18yLAogICAgICAgICAgICByZXNfcHZhbF9jYXJkZWM9cmVzX3B2YWxfY2FyZGVjLAogICAgICAgICAgICByZXNfcHZhbF9zY3ZpPXJlc19wdmFsX3NjdmksCiAgICAgICAgICAgIHJlc19wdmFsX2RjYT1yZXNfcHZhbF9kY2EsCiAgICAgICAgICAgIHJlc19wdmFsX21ubj1yZXNfcHZhbF9tbm4sCiAgICAgICAgICAgIHJlc19wdmFsX3NjYW5vcmFtYT1yZXNfcHZhbF9zY2Fub3JhbWEpLGZpbGU9InJlc19wdmFsX25ld19yZXZpc2VkLnJkcyIpI3Jlc19wdmFsX25ldy5yZHMKYGBgCgpgYGB7cn0KcmVzX3B2YWxfbGlzdD1yZWFkUkRTKCIuL3Jlc19wdmFsX25ld19yZXZpc2VkLnJkcyIpCmBgYAoKCiMgRmluYWwgUGxvdHMKCkluIHRoaXMgU2VjdGlvbiwgSSByZXBvcnRlZCB0aGUgcmVzcGVjdGl2ZSBwc2V1ZG90aW1lIGlkZW50aWZpZWQgYnkgY29tcGFyZWQgbWV0aG9kcywgaW5jbHVkaW5nIE1OTiwgc2NWSSwgRENBIGFuZCBzY2Fub3JhbWEKCiMjIEZlYXR1cmUgcGxvdHMgCgpgYGB7cn0Kb2xkPXRoZW1lX3NldCh0aGVtZV9idygpK3RoZW1lKHN0cmlwLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD0id2hpdGUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFuZWwuZ3JpZCA9ZWxlbWVudF9ibGFuaygpKSkKYGBgCgoKCmBgYHtyfQoKZGYwX3RtcDA9c3Vic2V0KGRmX3Bsb3QsIHZhcmlhYmxlPT0iT3ZlcmFsbCAoQ2hpc3EpIiAmIUdyb3VwICVpbiUiUmF3IikKCmdndGl0bGUwPWMoIkNhckRFQyI9Im9ial9jYXJkZWMiLCJEQ0EiPSJvYmpfZGNhIiwiTU5OIj0ib2JqX21ubiIsIlJhdyI9Im9ial9yYXciLCJzY1ZJIj0ib2JqX3NjdmkiLCJTY2Fub3JhbWEiPSJvYmpfc2Nhbm9yYW1hIikKZ2V0X3Bsb3QxPWZ1bmN0aW9uKGRmMDAsZ2VuZT0iUzEwMEE4Iix0aXRsZTA9IkNhckRFQyIpewogIHBfdmFsX2xhYmVsPWRmMF90bXAwJHB2YWxbZGYwX3RtcDAkZ2VuZT09Z2VuZSZkZjBfdG1wMCRHcm91cD09dGl0bGUwXQogIHA9Z2dwbG90KGRhdGEgPWRmMDAsYWVzX3N0cmluZyh4PSJwc2V1ZG90aW1lIix5PWdlbmUpKSsKICAgICAgZ2VvbV9wb2ludChhZXMoY29sb3I9QmF0Y2hJRCksc2l6ZT0wLjAxKSsKICAgICAgZ3VpZGVzKGNvbG9yPWd1aWRlX2xlZ2VuZChvdmVycmlkZS5hZXMgPSBsaXN0KHNpemU9NSkpKSsKICAgICAgZ2VvbV9zbW9vdGgoYWVzKGNvbG9yPUJhdGNoSUQpLG1ldGhvZD0iZ2FtIixmb3JtdWxhID0geSB+IHMoeCwgYnM9ImNzIikpKwogICAgICBnZW9tX3Ntb290aChjb2xvcj0iYmxhY2siLG1ldGhvZD0iZ2FtIixmb3JtdWxhID0geSB+IHMoeCwgYnM9ImNzIiksc2l6ZT0wLjUpKwogICAgICB5bGFiKHBhc3RlMChnZW5lLCIgKCIsYXMuY2hhcmFjdGVyKHRmX2RmJFZhcmlhbmNlVHlwZVt0Zl9kZiRHZW5lPT1nZW5lXSksIikiKSkrCiAgICAgIGdndGl0bGUodGl0bGUwLHN1YnRpdGxlID0gcGFzdGUwKCJwLXZhbHVlPSIsaWZlbHNlKGxlbmd0aChwX3ZhbF9sYWJlbCkhPTAsc2NhbGVzOjpzY2llbnRpZmljKHBfdmFsX2xhYmVsKSwiTkEiKSkpKwogICAgCiAgICB4bGFiKCJQc2V1ZG90aW1lIikrdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCxmYWNlPSJib2xkIixoanVzdD0wLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xNSxmYWNlPSJib2xkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoY29sb3I9aWZlbHNlKGFzLmNoYXJhY3Rlcih0Zl9kZiRWYXJpYW5jZVR5cGVbdGZfZGYkR2VuZT09Z2VuZV0pPT0iSFZHIiwicmVkIiwiYmx1ZSIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSkrc2NhbGVfY29sb3JfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpCiAgcmV0dXJuKHApCn0KCgpwbGlzdF8wPWxhcHBseShzb3J0KHRmX2RmJEdlbmUpLGZ1bmN0aW9uKGdlbmUwKXsKICBwMT1nZXRfcGxvdDEoZGYwX2xpc3Qkb2JqX2NhcmRlYyxnZW5lID0gZ2VuZTAsdGl0bGUwID0gIkNhckRFQyIpK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICBwMj1nZXRfcGxvdDEoZGYwX2xpc3Qkb2JqX2RjYSxnZW5lID0gZ2VuZTAsdGl0bGUwID0gIkRDQSIpK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICBwMz1nZXRfcGxvdDEoZGYwX2xpc3Qkb2JqX21ubixnZW5lID0gZ2VuZTAsdGl0bGUwID0gIk1OTiIpCiAgcDQ9Z2V0X3Bsb3QxKGRmMF9saXN0JG9ial9zY3ZpLGdlbmUgPSBnZW5lMCx0aXRsZTAgPSAic2NWSSIpK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICBwNT1nZXRfcGxvdDEoZGYwX2xpc3Qkb2JqX3NjYW5vcmFtYSxnZW5lID0gZ2VuZTAsdGl0bGUwID0gIlNjYW5vcmFtYSIpK3RoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKICByZXR1cm4obGlzdChwMSxwNSxwMixwNCxwMykpCn0pCmBgYAoKYGBge3IsZmlnLmhlaWdodD0zLGZpZy53aWR0aD0xNH0KcGxpc3RfMT1sYXBwbHkoMTpsZW5ndGgocGxpc3RfMCksZnVuY3Rpb24oeCkgZWdnOjpnZ2FycmFuZ2UocGxvdHMgPSBwbGlzdF8wW1t4XV0sbnJvdyA9IDEsZHJhdyA9IEYpKQpgYGAKCgpgYGB7cixmaWcuaGVpZ2h0PTMsZmlnLndpZHRoPTE0fQojSWYgd2UgbmVlZCBvdGhlciBnZW5lcyB3ZSBjYW4gc2F2ZSB0aGVtIHVzaW5nIGZvbGxvd2luZyBjb2RlcwojZm9yKCB4IGluIDE6bGVuZ3RoKHBsaXN0XzApKXsKIyAgZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKCIuL3JldmlzZWRfZmlndXJlcy9URnNfcHNldWRvdGltZS8iLHNvcnQodGZfZGYkR2VuZSlbeF0sIi5wZGYiKSxlZ2c6OmdnYXJyYW5nZShwbG90cyA9IHBsaXN0XzBbW3hdXSxucm93ID0gMSksd2lkdGggPSAjMTQsaGVpZ2h0ID0gMykKIyAgZ2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKCIuL3JldmlzZWRfZmlndXJlcy9URnNfcHNldWRvdGltZS8iLHNvcnQodGZfZGYkR2VuZSlbeF0sIi50aWZmIiksZWdnOjpnZ2FycmFuZ2UocGxvdHMgPSBwbGlzdF8wW1t4XV0sbnJvdyA9IDEpLHdpZHRoID0gIzE0LGhlaWdodCA9IDMsZHBpPTMwMCxjb21wcmVzc2lvbj0ibHp3IikKI30KYGBgCgpgYGB7cn0KaWQwPWMoMjMsMjIsMjUsMTkpCmBgYAoKYGBge3IsZmlnLndpZHRoPTE0LGZpZy5oZWlnaHQ9MTJ9CnBsb3RfZ3JpZChwbGlzdF8xW1syM11dLHBsaXN0XzFbWzIyXV0scGxpc3RfMVtbMjVdXSxwbGlzdF8xW1sxOV1dLGFsaWduID0gInYiLG5jb2wgPSAxKQpgYGAKCmBgYHtyfQpwbGlzdF8wMD1sYXBwbHkoc29ydCh0Zl9kZiRHZW5lKVtjKDIzLDIyLDI1LDE5KV0sZnVuY3Rpb24oZ2VuZTApewogIHAxPWdldF9wbG90MShkZjBfbGlzdCRvYmpfY2FyZGVjLGdlbmUgPSBnZW5lMCx0aXRsZTAgPSAiQ2FyREVDIikrdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSsKICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkscGxvdC5tYXJnaW4gPSB1bml0KGMoMC4yLDAsMC41LDAuMiksImNtIikpCiAgcDI9Z2V0X3Bsb3QxKGRmMF9saXN0JG9ial9kY2EsZ2VuZSA9IGdlbmUwLHRpdGxlMCA9ICJEQ0EiKSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpK3RoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgcDM9Z2V0X3Bsb3QxKGRmMF9saXN0JG9ial9tbm4sZ2VuZSA9IGdlbmUwLHRpdGxlMCA9ICJNTk4iKSt0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQogIHA0PWdldF9wbG90MShkZjBfbGlzdCRvYmpfc2N2aSxnZW5lID0gZ2VuZTAsdGl0bGUwID0gInNjVkkiKSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpK3RoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgcDU9Z2V0X3Bsb3QxKGRmMF9saXN0JG9ial9zY2Fub3JhbWEsZ2VuZSA9IGdlbmUwLHRpdGxlMCA9ICJTY2Fub3JhbWEiKSt0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpK3RoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCiAgcmV0dXJuKGxpc3QocDEscDUscDIscDQscDMpKQp9KQpgYGAKCmBgYHtyIGZpZy53aWR0aD0xNSxmaWcuaGVpZ2h0PTEyfQpwcDA9ZWdnOjpnZ2FycmFuZ2UocGxvdHMgPSBjKHBsaXN0XzAwW1sxXV0scGxpc3RfMDBbWzJdXSxwbGlzdF8wMFtbM11dLHBsaXN0XzAwW1s0XV0pLG5jb2wgPSA1LGRyYXcgPSBGKQpgYGAKCiMjIEhlYXRtYXAgcGxvdHMKCmBgYHtyLGZpZy53aWR0aD0xNixmaWcuaGVpZ2h0PTd9CnN1cHByZXNzUGFja2FnZVN0YXJ0dXBNZXNzYWdlcyhsaWJyYXJ5KGNvd3Bsb3QpKQpwLmRlbm9pc2VkPWdnZHJhdygpK2RyYXdfaW1hZ2UobWFnaWNrOjppbWFnZV9yZWFkX3BkZigiZGVub2lzZWRfaGVhdG1hcF9ub3Ntb290aC5wZGYiKSxzY2FsZT0xKQpwLnJhdz1nZ2RyYXcoKStkcmF3X2ltYWdlKG1hZ2ljazo6aW1hZ2VfcmVhZF9wZGYoIi4vcmF3X3BzZXVkb3RpbWVfaGVhdG1hcF9ub3Ntb290aC5wZGYiKSxzY2FsZT0xKQoKcF9oZWF0bWFwPWdnZHJhdygpK2RyYXdfcGxvdChwLmRlbm9pc2VkLHg9MCx5PTAsd2lkdGg9MC41LGhlaWdodD0wLjk4KSsKICBkcmF3X3Bsb3QocC5yYXcseD0wLjUseT0wLHdpZHRoPTAuNSxoZWlnaHQ9MC45OCkrCiAgZHJhd19sYWJlbCgiRGVub2lzZWQgZ2VuZSBleHByZXNzaW9uIChwc2V1ZG90aW1lIGJ5IENhckRFQykiLCB4PTAuMjUseT0xLGhqdXN0PTAuNSx2anVzdCA9IDEsc2l6ZT0yMCkrCiAgZHJhd19sYWJlbCgiUmF3IGdlbmUgZXhwcmVzc2lvbiAocHNldWRvdGltZSBieSBSYXcpIiwgeD0wLjc1LHk9MSxoanVzdD0wLjUsdmp1c3QgPSAxLHNpemU9MjApCgpgYGAKCmBgYHtyLGZpZy53aWR0aD0xOCxmaWcuaGVpZ2h0PTh9CnBfaGVhdG1hcApgYGAKCiMjIGJveHBsb3Qgb2YgcHZhbAoKYGBge3IsZmlnLndpZHRoPTgsZmlnLmhlaWdodD00fQpkZjBfdG1wPXN1YnNldChkZl9wbG90LGdlbmV0eXBlPT0iSFZHIiAmIHZhcmlhYmxlPT0iT3ZlcmFsbCAoQ2hpc3EpIiAmIUdyb3VwICVpbiUiUmF3IikKZGYwX3RtcCRHcm91cD1mYWN0b3IoZGYwX3RtcCRHcm91cCxsZXZlbHMgPSBjKCJDYXJERUMiLCJTY2Fub3JhbWEiLCJEQ0EiLCJzY1ZJIiwiTU5OIikpCgojZGYwX3RtcCU+JWdyb3VwX2J5KEdyb3VwKSU+JXN1bW1hcmlzZShuPW4oKSkKcDE9Z2dwbG90KGRmMF90bXAsCiAgICAgICBhZXMoeD1Hcm91cCx5PXB2YWxfbG9nMTAsZmlsbD1Hcm91cCkpKwogICNnZW9tX3Zpb2xpbihhZXMoeD12YXJpYWJsZSx5PXB2YWwpLHNjYWxlID0gIndpZHRoIixhZGp1c3Q9MSkrCiAgZ2VvbV9ib3hwbG90KHdpZHRoPTAuNSxjb2xvcj0iYmx1ZSIsb3V0bGllci5jb2xvciA9IE5BLHNpemU9MC4yLAogICAgICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuNikpKwogICNnZ3JlcGVsOjpnZW9tX2xhYmVsX3JlcGVsKGRhdGE9ZGZfcGxvdF9pbmZpbml0ZSxsYWJlbD0iZXJlIixwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDEpKSsKICBnZW9tX2ppdHRlcihhbHBoYT0wLjQsc2l6ZT0wLjQscG9zaXRpb249cG9zaXRpb25faml0dGVyZG9kZ2Uoaml0dGVyLndpZHRoID0gMC4xLGRvZGdlLndpZHRoID0gMC42KSkrCiAgI2dlb21fcG9pbnQoZGF0YT1kZl9wbG90JT4lZ3JvdXBfYnkodmFyaWFibGUsR3JvdXApJT4lc3VtbWFyaXNlKG1lYW49bWVhbihwdmFsKSksCiAgIyAgICAgICAgICBhZXMoeD12YXJpYWJsZSx5PW1lYW4pLHNpemU9Mixjb2xvcj0id2hpdGUiLHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMSkpKwogICB0aGVtZV9jb3dwbG90KCkrCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMixjb2xvcj0icmVkIixsdHk9MykrCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9MjAsaGp1c3Q9MSkpKwogIHlsYWIoZXhwcmVzc2lvbihwYXN0ZSgiLSIsbG9nWzEwXSwiKCIscCwiLiIsdmFsdWUsIikiKSkpKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1NZXRob2RzX2NvbG9yKSsKICB4bGFiKCIiKSsKICBnZ3RpdGxlKCJIVkdzICgyMyBnZW5lcykiKSsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3Q9MC41KSkKCmRmMF90bXA9c3Vic2V0KGRmX3Bsb3QsZ2VuZXR5cGU9PSJMVkciICYgdmFyaWFibGU9PSJPdmVyYWxsIChDaGlzcSkiICYhR3JvdXAgJWluJSJSYXciKQpkZjBfdG1wJEdyb3VwPWZhY3RvcihkZjBfdG1wJEdyb3VwLGxldmVscyA9IGMoIkNhckRFQyIsIlNjYW5vcmFtYSIsIkRDQSIsInNjVkkiLCJNTk4iKSkKCiNkZjBfdG1wJT4lZ3JvdXBfYnkoR3JvdXApJT4lc3VtbWFyaXNlKG49bigpKQpwMj1nZ3Bsb3QoZGYwX3RtcCwKICAgICAgIGFlcyh4PUdyb3VwLHk9cHZhbF9sb2cxMCxmaWxsPUdyb3VwKSkrCiAgI2dlb21fdmlvbGluKGFlcyh4PXZhcmlhYmxlLHk9cHZhbCksc2NhbGUgPSAid2lkdGgiLGFkanVzdD0xKSsKICBnZW9tX2JveHBsb3Qod2lkdGg9MC41LGNvbG9yPSJibHVlIixvdXRsaWVyLmNvbG9yID0gTkEsc2l6ZT0wLjIsCiAgICAgICAgICAgICAgIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMC42KSkrCiAgI2dncmVwZWw6Omdlb21fbGFiZWxfcmVwZWwoZGF0YT1kZl9wbG90X2luZmluaXRlLGxhYmVsPSJlcmUiLHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoMSkpKwogIGdlb21faml0dGVyKGFscGhhPTAuNCxzaXplPTAuNCxwb3NpdGlvbj1wb3NpdGlvbl9qaXR0ZXJkb2RnZShqaXR0ZXIud2lkdGggPSAwLjEsZG9kZ2Uud2lkdGggPSAwLjYpKSsKICAjZ2VvbV9wb2ludChkYXRhPWRmX3Bsb3QlPiVncm91cF9ieSh2YXJpYWJsZSxHcm91cCklPiVzdW1tYXJpc2UobWVhbj1tZWFuKHB2YWwpKSwKICAjICAgICAgICAgIGFlcyh4PXZhcmlhYmxlLHk9bWVhbiksc2l6ZT0yLGNvbG9yPSJ3aGl0ZSIscG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSgxKSkrCiAgIHRoZW1lX2Nvd3Bsb3QoKSsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAyLGNvbG9yPSJyZWQiLGx0eT0zKSsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT0yMCxoanVzdD0xKSkrCiAgeWxhYihleHByZXNzaW9uKHBhc3RlKCItIixsb2dbMTBdLCIoIixwLCIuIix2YWx1ZSwiKSIpKSkrCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPU1ldGhvZHNfY29sb3IpKwogIHhsYWIoIiIpKwogIGdndGl0bGUoIkxWR3MgKDM4IGdlbmVzKSIpKwogIHRoZW1lKGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSxsZWdlbmQucG9zaXRpb24gPSAibm9uZSIscGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUpKQogICNjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsMjApKQpgYGAKCmBgYHtyLGZpZy53aWR0aD00LjUsZmlnLmhlaWdodD0xMn0KcF9ib3g9cGxvdF9ncmlkKHAxLHAyLG5jb2w9MSxhbGlnbiA9ICJ2IikKcF9ib3gKYGBgCgotIHRoZSBtZWRpYW4gdmFsdWUgZm9yIGVhY2ggbWV0aG9kIGluIGFib3ZlIGZpZ3VyZQoKYGBge3J9CnN1YnNldChkZl9wbG90LCB2YXJpYWJsZT09Ik92ZXJhbGwgKENoaXNxKSIgJiFHcm91cCAlaW4lIlJhdyIpJT4lCiAgZ3JvdXBfYnkoZ2VuZXR5cGUsR3JvdXApJT4lCiAgc3VtbWFyaXNlKGxvZ19wdmFsX21lZGlhbj1tZWRpYW4ocHZhbF9sb2cxMCksCiAgICAgICAgICAgbG9ncF9tZWFuPW1lYW4ocHZhbF9sb2cxMCksCiAgICAgICAgICAgbj1uKCkpCmBgYAoKCiMjIEZpZ3VyZSA2CgpgYGB7cixmaWcud2lkdGg9MTgsZmlnLmhlaWdodD0xOH0Kd2lkdGg9MTgKaGVpZ2h0PTIwCnA2PWdnZHJhdygpK2RyYXdfcGxvdChwX2hlYXRtYXAseCA9IDAseSA9IDEtOC9oZWlnaHQsaGVpZ2h0ID0gOC9oZWlnaHQsd2lkdGggPSAxKSsKICBkcmF3X3Bsb3QocF9ib3gseD0wLHk9MC4xL2hlaWdodCxoZWlnaHQgPSAxMS44L2hlaWdodCx3aWR0aD00LjUvd2lkdGgpKwogIGRyYXdfcGxvdChwcDAseD00LjUvd2lkdGgseT0wLGhlaWdodCA9IDExLjcvaGVpZ2h0LHdpZHRoID0gMTMuNS93aWR0aCkrCiAgZHJhd19sYWJlbCgiYSIsIHg9MCx5PTEsaGp1c3Q9MCx2anVzdCA9IDEsc2l6ZT0zMCkrCiAgZHJhd19sYWJlbCgiYiIsIHg9MC41LHk9MSxoanVzdD0wLHZqdXN0ID0gMSxzaXplPTMwKSsKICBkcmF3X2xhYmVsKCJjIiwgeD0wLHk9MTIvaGVpZ2h0LGhqdXN0PTAsdmp1c3QgPSAxLHNpemU9MzApKwogIGRyYXdfbGFiZWwoImQiLCB4PTQuNS93aWR0aCx5PTEyL2hlaWdodCxoanVzdD0wLHZqdXN0ID0gMSxzaXplPTMwKSsKICBkcmF3X2xhYmVsKCJDYXJERUMiLCB4PSg0LjUrMTMuNS8xMCoxKzAuMikvd2lkdGgseT0xMS45L2hlaWdodCxoanVzdD0wLjUsdmp1c3QgPSAxLHNpemU9MTgsZm9udGZhY2UgPSAiYm9sZCIpKwogIGRyYXdfbGFiZWwoIlNjYW5vcmFtYSIsIHg9KDQuNSsxMy41LzEwKjMtMC4xKS93aWR0aCx5PTExLjkvaGVpZ2h0LGhqdXN0PTAuNSx2anVzdCA9IDEsc2l6ZT0xOCxmb250ZmFjZSA9ICJib2xkIikrCiAgZHJhd19sYWJlbCgiRENBIiwgeD0oNC41KzEzLjUvMTAqNS0wLjI3KS93aWR0aCx5PTExLjkvaGVpZ2h0LGhqdXN0PTAuNSx2anVzdCA9IDEsc2l6ZT0xOCxmb250ZmFjZSA9ICJib2xkIikrCiAgZHJhd19sYWJlbCgic2NWSSIsIHg9KDQuNSsxMy41LzEwKjctMC41KS93aWR0aCx5PTExLjkvaGVpZ2h0LGhqdXN0PTAuNSx2anVzdCA9IDEsc2l6ZT0xOCxmb250ZmFjZSA9ICJib2xkIikrCiAgZHJhd19sYWJlbCgiTU5OIix4PSg0LjUrMTMuNS8xMCo5LTAuNjgpL3dpZHRoLHk9MTEuOS9oZWlnaHQsaGp1c3Q9MC41LHZqdXN0ID0gMSxzaXplPTE4LGZvbnRmYWNlID0gImJvbGQiKQpwNgpgYGAKCmBgYHtyfQojZ2dzYXZlKGZpbGVuYW1lID0gIi4vcmV2aXNlZF9maWd1cmVzL0ZpZ3VyZTcucGRmIixwNix3aWR0aD0xOCxoZWlnaHQgPSAxOCkKI2dnc2F2ZShmaWxlbmFtZSA9ICIuL3JldmlzZWRfZmlndXJlcy9GaWd1cmU3LnRpZmYiLHA2LHdpZHRoPTE4LGhlaWdodCA9IDE4LGRwaT0zMDAsY29tcHJlc3Npb249Imx6dyIpCmBgYAoKCiMgSG93IGRvIHdlIGdldCBwdmFsIGluIGFib3ZlIEZpZy5jCgpXZSBwZXJmb3JtZWQgaHlwb3RoZXNpcyB0ZXN0cyByZWxhdGluZyB0byB0d28gZml0dGVkIGdhbSBvYmplY3RzIGhlcmUgYnkgdHdvIHdheXMuIAoKMS4gUmVnYXJkaW5nIGBCYXRjaElEYCBhcyBwcmVkaWN0b3IsIGFuZCB0aGVuIHdlIHBlcmZvcm1lZAogICAgLSBNb2RlbDE6IGBnYW0oZ2VuZX5zKHBzZXVkb3RpbWUsY3M9ImJzIikpYAogICAgLSBNb2RlbDI6IGBnYW0oZ2VuZX5CYXRjaElEK3MocHNldWRvdGltZSxjcz0iYnMiKSlgCiAgICAtIFVzaW5nIGBDaGlzcSB0ZXN0YCBhbmQgYEYgdGVzdGAgdG8gY29tcGFyZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIE1vZGVsMSBhbmQgTW9kZWwyLiAgIAoyLiBSZWdhcmRpbmcgYEJhdGNoSURgIGFzIHByZWRpY3Rvcu+8jGJ1dCB3ZSBjb21wYXJlZCBwYWlyd2lzZSBhbW9uZyBUMSB2cyBUMiwgVDEgVnMgVDMgYW5kICBUMiB2cyBUMywgc2VwYXJhdGVseS4gCgpTbyB3ZSBoYXZlIDggcC12YWx1ZSBmb3IgZWFjaCBnZW5lLiBCZWNhdXNlIHBzZXVkb3RpbWUgY2FuIGluZmVyZW5jZWQgYnkgZGVub2lzZWQgZ2VuZSBleHByZXNzaW9uIGZyb20gQ2FyREVDIG9yIHJhdyBnZW5lIGV4cHJlc3Npb24sIHdlIG1heWJlIGhhdmUgdHdvIHJlc3VsdHM6ICAgCgotIFVzaW5nIHRoZSBwc2V1ZG90aW1lIGluZmVyZW5jZWQgYnkgQ2FyREVDLCB3ZSBjb21wYXJlZCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGRlbm9pc2VkIGdlbmUgZXhwcmVzc2lvbiBhbmQgcmF3IGdlbmUgZXhwcmVzc2lvbi4gIAotIFVzaW5nIHRoZSBwc2V1ZG90aW1lIGluZmVyZW5jZWQgYnkgQ2FyREVDIGFuZCByYXcgZ2VuZSBleHByZXNzaW9uLCB3ZSBjb21wYXJlZCB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGRlbm9pc2VkIGdlbmUgZXhwcmVzc2lvbiBhbmQgcmF3IGdlbmUgZXhwcmVzc2lvbi4gCgpgYGB7cn0Kc2Vzc2lvbkluZm8oKQpgYGAKCgoK